knitr::opts_chunk$set(warning = FALSE, message = FALSE)
library(tidyverse) # general
library(ggalt) # dumbbell plots
library(grid) # plots
library(gridExtra) # plots
library(ggcorrplot) 
library(ggplot2)
library(rworldmap) # quick country-level heat map
library(countrycode) # continent
library(broom) # significant trends within countries
library(lubridate) # dealing with dates
library(car)
library(scales)

1.Obtaining Data

1.1 Suicide Data

The dataset was sourced from links and contains a comprehensive collection of suicide statistics broken down by country, year, age, and sex. This data has been gathered from multiple datasets with the intention of identifying trends and patterns in global suicide rates. Here are the features of this dataset:

  • Country: The country where the data was recorded.

  • Year: The year when the data was recorded. The data was collect from 1985 until 2016.

  • Sex: The sex of the individuals included in the suicide count.

  • Age: The age group of the individuals included in the suicide count.

  • Suicide_no: The total number of suicides recorded in a specific country, year, age group, and sex.

  • Population: The total population of the specific age and sex group in the country for that year.

  • Suicide/ 100k pop: This is a derived metric representing the number of suicides per 100,000 people in the population, calculated as (Suicide_no / Population) * 100000.

  • HDI_for_year: Human Development Index, a statistic composite index of life expectancy, education, and per capita income indicators.

  • GDP_for_year ($): The gross domestic product (GDP) of the country for the specified year, measured in US dollars.

  • GDP_per_capita ($): The GDP per capita of the country for the specified year, also measured in US dollars. It is calculated by dividing the GDP_for_year ($) by the total population of the country.

  • Generation: The generation cohort of the individuals included in the suicide count (e.g., Gen X, Boomers, etc.).

suicide_data <- read_csv("suicide_data.csv", show_col_types = FALSE)
head(suicide_data)
dim(suicide_data)
[1] 27820    12

We further supplemented our analysis by incorporating additional features, which we hypothesize may significantly impact suicide rates.

1.2 Continet

We’ve enriched our dataset by appending a ‘continent’ feature corresponding to each country. This enhancement, accomplished utilizing the ‘countrycode’ library, will facilitate subsequent geographical analyses.

# getting continent data:
suicide_data$continent <- countrycode(sourcevar = suicide_data$country,
                              origin = "country.name",
                              destination = "continent")

dim(suicide_data)
[1] 27820    13

1.3 Life Expectancy

We also added ‘life expectancy’ data from links. Remember, the Human Development Index (HDI) also measures life expectancy, so these two features might be highly correlated. We’ll keep this in mind for our analysis.

life_exp_data <-read_csv('life_exp.csv',show_col_types = FALSE)
head(life_exp_data)
life_exp_data$Time <- as.integer(life_exp_data$Time)

life_exp_data <- life_exp_data %>%
  rename( life_exp = `Value`,
          year = `Time`,
          country = `Country Name`) %>%
  as.data.frame()

We’ve identified that the format of some country names differs between the suicide data and the life expectancy dataset. To prevent any further discrepancies, it’s imperative that we address and rectify these inconsistencies.

name_changes_life <- c("Bahamas, The" = "Bahamas", 
                  "Czechia" = "Czech Republic",
                  "Kyrgyz Republic" = "Kyrgyzstan",
                  "Macao SAR, China" = "Macau",
                  "Korea, Rep." = "Republic of Korea",
                  "St. Kitts and Nevis" = "Saint Kitts and Nevis",
                  "St. Lucia" = "Saint Lucia",
                  "St. Vincent and the Grenadines" = "Saint Vincent and Grenadines",
                  "Slovak Republic" = "Slovakia",
                  "Turkiye" = "Turkey"
                  )

life_exp_data$country[life_exp_data$country %in% names(name_changes_life)] <- 
    name_changes_life[life_exp_data$country[life_exp_data$country %in% names(name_changes_life)]]
data <- suicide_data %>%
  left_join(life_exp_data[, c('year', 'country', 'life_exp')], 
            by = c('year', 'country'))
dim(data)
[1] 27820    14

1.4 Temperature

Our investigation suggests that temperature might influence suicide rates.(source: links) So, we incorporated temperature data into our dataset from link to further explore this possibility. This dataset covers temperature data since 1750 till 2013. We include three additional features:

  • max_temp: The highest monthly temperature for each year.
  • min_temp: The lowest monthly temperature for each year.
  • avg_temp: The average monthly temperature for each year.
temp_data <- read_csv("GlobalLandTemperaturesByCountry.csv",show_col_types = FALSE)
head(temp_data)
# Extract year and month from date_column
temp_data$year <- year(temp_data$dt)
temp_data$month <- month(temp_data$dt)

temp_data <- select(temp_data, -dt, -"AverageTemperatureUncertainty")
temp_data<- rename(temp_data, country = Country)

temp_data$year <-as.integer(temp_data$year)
temp_data$month <-as.integer(temp_data$month)
temp_data$AverageTemperature <-as.numeric(temp_data$AverageTemperature)

temp_data <- temp_data %>%
  filter(year >= 1985 & year <= 2016)

temp_data <- temp_data %>%
  group_by(country, year) %>%
  mutate(
    avg_temp = sum(AverageTemperature, na.rm = TRUE) / sum(!is.na(AverageTemperature)),
    max_temp = max(AverageTemperature, na.rm = TRUE),
    min_temp = min(AverageTemperature, na.rm = TRUE)
  ) %>%
  ungroup()
Warning: There were 58 warnings in `mutate()`.
The first warning was:
ℹ In argument: `max_temp = max(AverageTemperature, na.rm = TRUE)`.
ℹ In group 233: `country = "Antarctica"`, `year = 1985`.
Caused by warning in `max()`:
! no non-missing arguments to max; returning -Inf
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 57 remaining warnings.
temp_data <- select(temp_data, -month, -"AverageTemperature")

temp_data <-distinct(temp_data)

temp_data <- temp_data %>%
  filter_all(all_vars(!is.infinite(.)))

As with the life expectancy data, we’ve noticed discrepancies in the formatting of country names between the suicide data and temperature dataset. To avoid any potential issues down the line, it’s crucial that we reconcile these inconsistencies and standardize the country names across all datasets.

name_changes_temp <- c("Antigua And Barbuda" = "Antigua and Barbuda", 
                  "Bosnia And Herzegovina" = "Bosnia and Herzegovina",
                  "South Korea" = "Republic of Korea",
                  "Russia" = "Russian Federation",
                  "Saint Kitts And Nevis" = "Saint Kitts and Nevis",
                  "Trinidad And Tobago" = "Trinidad and Tobago",
                  "Saint Vincent And The Grenadines" = "Saint Vincent and Grenadines"
                  )
temp_data$country[temp_data$country %in% names(name_changes_temp)] <- 
    name_changes_temp[temp_data$country[temp_data$country %in% names(name_changes_temp)]]
data <- data %>%
  left_join(temp_data[, c('year', 'country', 'avg_temp', 'max_temp', 'min_temp')], 
            by = c('year', 'country'))
dim(data)
[1] 27820    17

Now that we’ve collected all the necessary data, our next step is to clean and preprocess this data for further analysis.

2.Clean and Filter Data

glimpse(head(data, 8))
Rows: 8
Columns: 17
$ country              <chr> "Albania", "Albania", "Albania", "Albania", "Albania", "Alban…
$ year                 <dbl> 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987
$ sex                  <chr> "male", "male", "female", "male", "male", "female", "female",…
$ age                  <chr> "15-24 years", "35-54 years", "15-24 years", "75+ years", "25…
$ suicides_no          <dbl> 21, 16, 14, 1, 9, 1, 6, 4
$ population           <dbl> 312900, 308000, 289700, 21800, 274300, 35600, 278800, 257200
$ `suicides/100k pop`  <dbl> 6.71, 5.19, 4.83, 4.59, 3.28, 2.81, 2.15, 1.56
$ `country-year`       <chr> "Albania1987", "Albania1987", "Albania1987", "Albania1987", "…
$ `HDI for year`       <dbl> NA, NA, NA, NA, NA, NA, NA, NA
$ `gdp_for_year ($)`   <dbl> 2156624900, 2156624900, 2156624900, 2156624900, 2156624900, 2…
$ `gdp_per_capita ($)` <dbl> 796, 796, 796, 796, 796, 796, 796, 796
$ generation           <chr> "Generation X", "Silent", "Generation X", "G.I. Generation", …
$ continent            <chr> "Europe", "Europe", "Europe", "Europe", "Europe", "Europe", "…
$ life_exp             <dbl> 72.352, 72.352, 72.352, 72.352, 72.352, 72.352, 72.352, 72.352
$ avg_temp             <dbl> 12.99658, 12.99658, 12.99658, 12.99658, 12.99658, 12.99658, 1…
$ max_temp             <dbl> 24.084, 24.084, 24.084, 24.084, 24.084, 24.084, 24.084, 24.084
$ min_temp             <dbl> 3.304, 3.304, 3.304, 3.304, 3.304, 3.304, 3.304, 3.304
print(colnames(data))
 [1] "country"            "year"               "sex"                "age"               
 [5] "suicides_no"        "population"         "suicides/100k pop"  "country-year"      
 [9] "HDI for year"       "gdp_for_year ($)"   "gdp_per_capita ($)" "generation"        
[13] "continent"          "life_exp"           "avg_temp"           "max_temp"          
[17] "min_temp"          
sapply(data, function(x) length(unique(x)))
           country               year                sex                age 
               101                 32                  2                  6 
       suicides_no         population  suicides/100k pop       country-year 
              2084              25564               5298               2321 
      HDI for year   gdp_for_year ($) gdp_per_capita ($)         generation 
               306               2321               2233                  6 
         continent           life_exp           avg_temp           max_temp 
                 5               2177               2146               2011 
          min_temp 
              2082 

2.1 Columns and Values

data <- data %>% 
  rename(suicide_ratio = `suicides/100k pop`, 
         country_year = `country-year`,
         HDI_for_year = `HDI for year`,
         GDP_for_year = `gdp_for_year ($)`, 
         GDP_per_capita = `gdp_per_capita ($)`) %>%
  as.data.frame()

data$age <- gsub(" years", "", data$age)

data$sex <- ifelse(data$sex == "male", "Male", "Female")

2.2 Missing Values

2.2.1 Data Scarcity by Country/Year

In an ideal dataset, every unique combination of country and year (country_year) would be represented by 12 entries (2 genders across 6 age groups). Now, we need to verify the completeness of our data for each country_year combination.

data %>%
  group_by(country_year) %>%
  count() %>% #this SHOULD give 12 rows for every county-year combination (6 age bands * 2 gender)
  filter(n != 12)

It appears that there is problem with 2016 data.

year_value_counts <- as.data.frame(sort(table(data$year), decreasing = FALSE))
names(year_value_counts) <- c("Year", "Count")
head(year_value_counts,5)

Our exploration reveals that the dataset for the year 2016 is not only sparse, but also incomplete for the few countries that have entries. Additionally, data for the years between 1985 and 1989 are also quite limited. These issues need to be addressed. As a solution, we’ve decided to exclude the data from 2016. We also drop the ‘country_year’ column from our dataset.

data <- data %>%
  filter(year != 2016) %>% # I therefore exclude 2016 data
  select(-country_year)

In the following step, we focus on filtering our dataset to ensure its robustness for further analysis. Specifically, we are addressing the issue of certain countries that have insufficient data spread across the years. These sparse data points can potentially skew our analysis or generate inaccurate insights. Therefore, we will systematically remove such countries from our dataset to maintain data integrity and reliability for subsequent steps in our study.

minimum_years <- data %>%
  group_by(country) %>%
  summarize(rows = n(), 
            years = rows / 12) %>%
  arrange(years)

minimum_years <- minimum_years %>%
  filter(minimum_years$years<=3)
  

data <- data %>%
filter(!(country %in% minimum_years$country))

dim(data)
[1] 27492    16
sapply(data, function(x) length(unique(x)))
       country           year            sex            age    suicides_no     population 
            93             31              2              6           2083          25292 
 suicide_ratio   HDI_for_year   GDP_for_year GDP_per_capita     generation      continent 
          5282            306           2291           2205              6              5 
      life_exp       avg_temp       max_temp       min_temp 
          2166           2135           2000           2073 

We’ve further refined our dataset, eliminating data from 2016 and from eight countries due to their sparse or incomplete data. This ensures a more reliable basis for our analysis

2.2.2 NA Values

Once we’ve eliminated the incomplete data, we’ll proceed to inspect each feature for the presence of null values.

na_counts <- sapply(data, function(x) sum(is.na(x))/nrow(data)*100)
na_counts_df <- data.frame(Feature = names(na_counts), NA_ratio = na_counts)
na_counts_df = na_counts_df %>% `rownames<-`( NULL )
print(na_counts_df)

Approximately 70% of the ‘HDI_for_year’ column contains null values, necessitating an adjustment. Despite comprehensive exploration, we were unable to find any reliable data to fill these null values in the HDI. Additionally, since the formula for calculating the HDI changed in 2010, the index before and after this year is not directly comparable. As we’ve added life expectancy data for each year, the decision has been made to drop the ‘HDI_for_year’ column.

data = subset(data, select = -c(HDI_for_year) )

Approximately 7.5% of the temperature data consists of null values which requires addressing. As a first step, we’ll identify the countries that contain null values in their temperature data.

data %>% 
  group_by(country) %>% 
  filter(all(is.na(min_temp))) %>% 
  pull(country) %>% 
  unique()
[1] "Maldives"

There’s only one country, the Maldives (located in South Asia), for which temperature data is unavailable. Given that we have ample data for Asia, we’ve made the decision to exclude the Maldives from our dataset.

data %>%
  group_by(continent) %>%
  summarise(num_countries = n_distinct(country))
countries_to_remove <- c("Maldives")

data <- data[!data$country %in% countries_to_remove, ]

Given that our temperature data concludes in 2013, we will fill the ‘avg_temp’, ‘min_temp’, and ‘max_temp’ fields for the years 2014 and 2015 using the corresponding data from 2013.

We observed that the data for Ukraine in 2013 is missing from our suicide dataset. Consequently, to address this absence, we will use the data from 2012 for this particular country to approximate the values for 2014 and 2015.

df_2013 <- data %>%
  filter(year == 2013) %>%
  select(country, avg_temp, min_temp, max_temp)

names(df_2013)[2:4] <- paste0(names(df_2013)[2:4], "_2013")

df_2012 <- data %>%
  filter(year == 2012) %>%
  select(country, avg_temp, min_temp, max_temp)

names(df_2012)[2:4] <- paste0(names(df_2012)[2:4], "_2012")

# Replace NA values in 2014 and 2015 using the lookup table
data <- data %>%
  mutate(year = as.character(year)) %>%
  rowwise() %>%
  mutate(
    avg_temp = ifelse(year %in% c("2014", "2015") & is.na(avg_temp) & country != "Ukraine",
                      df_2013$avg_temp_2013[df_2013$country == country],
                      ifelse(year %in% c("2014", "2015") & is.na(avg_temp) & country == "Ukraine",
                             df_2012$avg_temp_2012[df_2012$country == country],
                             avg_temp)),
    min_temp = ifelse(year %in% c("2014", "2015") & is.na(min_temp) & country != "Ukraine",
                      df_2013$min_temp_2013[df_2013$country == country],
                      ifelse(year %in% c("2014", "2015") & is.na(min_temp) & country == "Ukraine",
                             df_2012$min_temp_2012[df_2012$country == country],
                             min_temp)),
    max_temp = ifelse(year %in% c("2014", "2015") & is.na(max_temp) & country != "Ukraine",
                      df_2013$max_temp_2013[df_2013$country == country],
                      ifelse(year %in% c("2014", "2015") & is.na(max_temp) & country == "Ukraine",
                             df_2012$max_temp_2012[df_2012$country == country],
                             max_temp))
  ) %>%
  ungroup()
data <- data %>%
  mutate(year = as.integer(year))
dim(data)
[1] 27372    15
na_counts <- sapply(data, function(x) sum(is.na(x))/nrow(data)*100)
na_counts_df <- data.frame(Feature = names(na_counts), NA_ratio = na_counts)
na_counts_df = na_counts_df %>% `rownames<-`( NULL )
print(filter(na_counts_df, NA_ratio>0))

With all missing values effectively handled, our dataset is now clean and ready for further analysis.

2.3 Factorizing Categorical Data

# Nominal factors
data_nominal <- c('country', 'sex', 'continent')

data[data_nominal] <- lapply(data[data_nominal], function(x){factor(x)})


# Making age ordinal
data$age <- factor(data$age, 
                   ordered = T, 
                   levels = c("5-14",
                              "15-24", 
                              "25-34", 
                              "35-54", 
                              "55-74", 
                              "75+"))

# Making generation ordinal
data$generation <- factor(data$generation, 
                   ordered = T, 
                   levels = c("G.I. Generation", 
                              "Silent",
                              "Boomers", 
                              "Generation X", 
                              "Millenials", 
                              "Generation Z"))

data <- as_tibble(data)

2.4 Outliers

Detecting and addressing outliers is a fundamental step in data preprocessing, especially for linear regression models that are significantly influenced by outliers.

Several methods exist to identify outliers, including:

  • Visual Inspection using Boxplots and Scatterplots: These plots offer a straightforward way to visually identify outliers. Boxplots are particularly useful for univariate analysis, while scatterplots facilitate bivariate analysis.

  • Z-Score Method: This technique labels any data point that deviates more than three standard deviations from the mean as an outlier. However, this method is only effective when the data is completely or nearly normally distributed. Hence, it’s not ideal for skewed data.

  • Tukey’s Fences:It is calculated by creating a “fence” boundary a distance of 1.5 IQR beyond the 1st and 3rd quartiles. Any data beyond these fences are considered to be outliers.This method provides a robust mechanism to spot outliers, even in skewed distributions.

Once outliers are identified, we can employ several strategies to handle them, such as:

  • Rescaling and Transforming Data: Techniques such as log transformation, square root transformation, or cube root transformation can help lessen the data skewness and mitigate the effects of outliers.

  • Truncation or Winsorization: This method caps the outliers at a specified percentile of the data, like the 5th or 95th percentile.

  • Removing Outliers: In extreme cases, when we are confident that an outlier arises from incorrect data entry or measurement, we might decide to eliminate these values to avoid their undue influence on our model. However, this method should be a last resort, as it might result in information loss and should be justified thoroughly.

Let’s begin by examining the distribution and range of our features to better understand the spread and dispersion of our data.

summary(data)
      country           year          sex           age        suicides_no   
 Argentina:  372   Min.   :1985   Female:13686   5-14 :4562   Min.   :    0  
 Austria  :  372   1st Qu.:1994   Male  :13686   15-24:4562   1st Qu.:    3  
 Belgium  :  372   Median :2002                  25-34:4562   Median :   26  
 Brazil   :  372   Mean   :2001                  35-54:4562   Mean   :  246  
 Chile    :  372   3rd Qu.:2008                  55-74:4562   3rd Qu.:  134  
 Colombia :  372   Max.   :2015                  75+  :4562   Max.   :22338  
 (Other)  :25140                                                             
   population       suicide_ratio       GDP_for_year       GDP_per_capita  
 Min.   :     278   Min.   :  0.0000   Min.   :4.692e+07   Min.   :   251  
 1st Qu.:  101399   1st Qu.:  0.9775   1st Qu.:9.210e+09   1st Qu.:  3424  
 Median :  441232   Median :  6.0900   Median :4.921e+10   Median :  9378  
 Mean   : 1869421   Mean   : 12.9199   Mean   :4.517e+11   Mean   : 16850  
 3rd Qu.: 1512871   3rd Qu.: 16.7625   3rd Qu.:2.627e+11   3rd Qu.: 24922  
 Max.   :43805214   Max.   :224.9700   Max.   :1.812e+13   Max.   :126352  
                                                                           
           generation      continent        life_exp        avg_temp          max_temp     
 G.I. Generation:2724   Africa  :  828   Min.   :53.98   Min.   :-18.085   Min.   :-2.065  
 Silent         :6262   Americas: 9156   1st Qu.:70.89   1st Qu.:  9.469   1st Qu.:19.398  
 Boomers        :4908   Asia    : 5148   Median :74.57   Median : 14.165   Median :24.004  
 Generation X   :6314   Europe  :11268   Mean   :74.10   Mean   : 15.888   Mean   :23.383  
 Millenials     :5714   Oceania :  972   3rd Qu.:77.74   3rd Qu.: 25.743   3rd Qu.:27.966  
 Generation Z   :1450                    Max.   :83.79   Max.   : 29.671   Max.   :38.842  
                                                                                           
    min_temp      
 Min.   :-33.783  
 1st Qu.: -2.155  
 Median :  5.740  
 Mean   :  7.952  
 3rd Qu.: 22.701  
 Max.   : 27.492  
                  

2.4.1 Visualize Distribution of the Data


# Set the overall layout for the combined plot
par(mfrow = c(3, 4))
par(mar = c(2, 2, 2, 2))  # Adjust the margins for each plot

# for each column in the dataframe

for(col in names(data)) {
  # if it's a numeric column
  if(is.numeric(data[[col]])) {
    # create a histogram
    hist(data[[col]], main=col, xlab=col, col = "#13527a", border = "#ebebeb", cex.main = 1)
  }
}

2.4.2 BoxPlots

# Set the overall layout for the combined plot
par(mfrow = c(2, 4))
par(mar = c(2, 2, 2, 2))  # Adjust the margins for each plot

# for each column in the dataframe

for(col in names(data)) {
  # if it's a numeric column
  if(is.numeric(data[[col]])) {
    # create a histogram
    boxplot(data[[col]], main=col, xlab=col, col = "#ebebeb", border = "#13527a", cex.main = 1)
  }
}

NA

From the above boxplots, it’s evident that “suicides_no”, “population”, and “GDP_for_year” all exhibit a significant number of outliers. Additionally, “suicide_ratio” and “GDP_per_capita” also show a substantial number of outlier values.

Our examination of the boxplots and histograms reveals a significant concentration of data within the ‘suicide_no’ and ‘suicide_ratio’ parameters, skewed towards zero. This high density around zero manifests as a long tail in the distribution towards the right. In order to address this skewness, we need to delve deeper into the records where ‘suicide_no’ is recorded as zero.

# Define a common theme
common_theme <- theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.title = element_text(size = 12),
    legend.position = "none",
    panel.grid.major = element_line(color = "grey", linewidth = 0.1),
    panel.grid.minor = element_blank()
  )

# Define a common color
common_color <- "steelblue"

# Data for the first plot
zero_suicides_data <- data %>%
  filter(suicides_no == 0) %>%
  group_by(age) %>%
  summarise(count = n()) %>%
  ggplot(aes(x = age, y = count, fill=age)) +
    geom_bar(stat = "identity") +
    labs(x = "Age", y = "Count", 
         title = "Zero Suicides by Age Group") +
    common_theme

# Data for the second plot
age_plot <- data %>%
  group_by(age) %>%
  summarize(suicide_per_100k = (sum(as.numeric(suicides_no)) / sum(as.numeric(population))) * 100000) %>%
  ggplot(aes(x = age, y = suicide_per_100k, fill = age)) + 
  geom_bar(stat = "identity") + 
  labs(title = "Global suicides per 100k, by Age",
       x = "Age", 
       y = "Suicides per 100k") +
  common_theme +
  scale_y_continuous(breaks = seq(0, 30, 1), minor_breaks = NULL) # Changed breaks for better visibility

# Arrange the plots
grid.arrange(age_plot, zero_suicides_data, ncol = 2)

Our observations suggest that a significant proportion of zero-suicide instances are within the 5-14 age bracket. The overall suicide average for this age group is notably low, generally under one.

This age group, however, doesn’t adequately represent the larger population. The reasons for suicide within this age bracket are likely to be fundamentally different from those of other groups, potentially influenced by unique causes.

Given these considerations, we have opted to exclude the 5-14 age group from our data. This decision stems from the realization that this group exhibits distinctly different behaviors and is not representative of the broader population in the context of suicide rates.

data <- data%>%
  filter(age != '5-14')
dim(data)
[1] 22810    15

2.4.3 Tukey’s Fences

As indicated by the histograms above, most of our data does not adhere to a normal distribution. To yield more specific results, we will initially employ Tukey’s Fences method to identify the number of outliers in each feature.

#check the number of outliers in each features

# Define the outlier_count function
Tukey_outlier_count <- function(col) {
  q75 <- quantile(col, 0.75, na.rm= TRUE)
  q25 <- quantile(col, 0.25, na.rm= TRUE)
  iqr <- q75 - q25
  min_val <- q25 - (iqr * 1.5)
  max_val <- q75 + (iqr * 1.5)
  outlier_count <- sum(col > max_val | col < min_val)
  outlier_percent <- round(outlier_count / length(col) * 100, 2)
  return(c(outlier_count, outlier_percent))
}

# Get numeric data
numeric_data <- data[, sapply(data, is.numeric)]

# Apply the function to numeric columns
outliers <- sapply(numeric_data, Tukey_outlier_count)

# Convert to dataframe
outliers_df <- as.data.frame(t(outliers))
colnames(outliers_df) <- c("Outlier_Count", "Outlier_Percent")

# Print the result
print(outliers_df)

2.4.4 Transformations

The results from the Tukey’s method, consistent with our boxplot observations, reveal a significant proportion of outliers in the “suicides_no”, “suicide_ratio”, “population”, and “GDP_for_year” data. In an attempt to reduce the impact of these outliers, we plan to enrich our dataset with additional columns, each representing log-transformed and square root-transformed values of these variables.

However, we face a challenge with the “suicide_no” and “suicide_ratio” variables as they contain zero values, making it impossible to apply a straightforward log transformation. To circumvent this issue, we’ll introduce an adjustment factor, a constant c=1, to all suicide numbers. Subsequently, we’ll compute a new ratio and apply a log transformation to it. This approach ensures a smooth and successful transformation process.

# Add a small constant to avoid undefined log values
c <- 1

data <- data %>%
  mutate(new_suicides_no = suicides_no + c,
         new_suicide_ratio = new_suicides_no / population,
         log_population = log(population),
         log_GDP_year = log(GDP_for_year),
         log_GDP_capita = log(GDP_per_capita),
         log_suicide_no = log(new_suicides_no),
         log_suicide_ratio = log(new_suicide_ratio)
         )

In our analysis, we opted for the natural logarithm for its ease of interpretation. While logarithmic transformations with different bases don’t alter the distribution’s form, they do have implications for how we interpret the coefficients in our model.

With the natural logarithm (base e), coefficients in a model where both the predictor (x) and response (y) variables are log-transformed indicate the percentage change in y corresponding to a 1% change in x.

On the other hand, if a base-10 logarithm were used in the same circumstances, each coefficient would represent the change in y associated with a 10% change in x.

Therefore, by using the natural logarithm, we simplify the interpretation of our model’s output, enabling more straightforward conclusions and discussions.

data <- data %>%
  mutate(sqrt_population = sqrt(population),
         sqrt_GDP_year = sqrt(GDP_for_year),
         sqrt_GDP_capita = sqrt(GDP_per_capita),
         sqrt_suicide_no = sqrt(suicides_no),
         sqrt_suicide_ratio = sqrt(suicide_ratio)
         )
# Define the list of columns to be processed
transformed_col = c("population","log_population", "sqrt_population",
                    "GDP_for_year", "log_GDP_year", "sqrt_GDP_year", 
                    "GDP_per_capita", "log_GDP_capita", "sqrt_GDP_capita" , 
                    "suicides_no", "log_suicide_no", "sqrt_suicide_no",
                    "suicide_ratio","log_suicide_ratio", "sqrt_suicide_ratio")

# Apply the function to numeric columns
transformed_data <- data[, transformed_col]
transformed_outliers <- sapply(transformed_data, Tukey_outlier_count)

# Convert to dataframe
outliers_df <- as.data.frame(t(transformed_outliers))
colnames(outliers_df) <- c("Outlier_Count", "Outlier_Percent")

# Print the result
print(outliers_df)

# Set the overall layout for the combined plot
par(mfrow = c(3, 4))
par(mar = c(2, 2, 2, 2))  # Adjust the margins for each plot

# for each column in the dataframe

for(col in names(data)) {
  # if it's a numeric column
  if(is.numeric(data[[col]])) {
    # create a histogram
    hist(data[[col]], main=col, xlab=col, col = "#13527a", border = "#ebebeb", cex.main = 1)
  }
}

Upon applying the log transformation, we noticed a remarkable decrease in the number of outliers. Moreover, the transformed data showed a tendency towards a more normal distribution, indicating the effectiveness of the transformation.

With the data distribution now less skewed and more akin to a normal distribution, we leveraged the Z-score method to further quantify the remaining outliers in each column. This allowed us a more precise examination of the data spread and outlier prevalence.

#data = subset(data, select = -c(sqrt_population,
#                                sqrt_GDP_year,
#                                sqrt_GDP_capita,
#                                sqrt_suicide_no,
#                                sqrt_suicide_ratio))

2.4.5 Z_Score

Next, we employ the Z-Score method to identify potential outliers within each feature. To do this, we’ll establish upper and lower bounds, beyond which a data point will be classified as an outlier. The calculations for these boundaries are as follows:

Upper limit: Mean + (3 * Standard Deviation) Lower limit: Mean - (3 * Standard Deviation)

This method is based on the principle that for a normally distributed dataset, about 99.7% of data falls within three standard deviations from the mean. Hence, any data point beyond this range can be considered an outlier.

Z_Score <- function(col){
  return((col - mean(col)) / sd(col))
}

Z_outlier_count <- function(col) {
  Upper_limit = mean(col) + (3 * sd(col))
  Lower_limit = mean(col) - (3 * sd(col))
  outlier_count <- sum(col > Upper_limit| col < Lower_limit)
  outlier_percent <- round(outlier_count / length(col) * 100, 2)
  return(c(outlier_count, outlier_percent))
}

# Get numeric data
numeric_data <- data[, sapply(data, is.numeric)]

# Apply the function to numeric columns
outliers <- sapply(numeric_data, Z_outlier_count)

# Convert to dataframe
outliers_df <- as.data.frame(t(outliers))
colnames(outliers_df) <- c("Outlier_Count", "Outlier_Percent")

# Print the result
print(outliers_df)

2.4.6 Exploring Outliers

In our project, “suicide_ratio” is the key variable we aim to predict using linear regression. It is important to acknowledge that linear regression models are particularly susceptible to the influence of outliers. Therefore, it is essential to adequately address and manage any outliers present in the “suicide_ratio” variable, to ensure our model’s accuracy and reliability.

let’s explore outliers in suicide ratio and check if there is any pattern in them. we use tukey’s fence dtected outliers.(to be edited)

# Calculate IQR and fences
Q1 <- quantile(data$suicide_ratio, 0.25)
Q3 <- quantile(data$suicide_ratio, 0.75)
IQR <- Q3 - Q1

lower_fence <- Q1 - 1.5 * IQR
upper_fence <- Q3 + 1.5 * IQR

# Filter outliers
suicide_outliers <- data %>% 
  filter(suicide_ratio < lower_fence | suicide_ratio > upper_fence)
summary(data)
      country           year          sex           age        suicides_no     
 Argentina:  310   Min.   :1985   Female:11405   5-14 :   0   Min.   :    0.0  
 Austria  :  310   1st Qu.:1994   Male  :11405   15-24:4562   1st Qu.:    6.0  
 Belgium  :  310   Median :2002                  25-34:4562   Median :   42.0  
 Brazil   :  310   Mean   :2001                  35-54:4562   Mean   :  292.9  
 Chile    :  310   3rd Qu.:2008                  55-74:4562   3rd Qu.:  180.0  
 Colombia :  310   Max.   :2015                  75+  :4562   Max.   :22338.0  
 (Other)  :20950                                                               
   population       suicide_ratio     GDP_for_year       GDP_per_capita  
 Min.   :     278   Min.   :  0.00   Min.   :4.692e+07   Min.   :   251  
 1st Qu.:   97662   1st Qu.:  2.99   1st Qu.:9.210e+09   1st Qu.:  3424  
 Median :  431350   Median :  8.58   Median :4.921e+10   Median :  9378  
 Mean   : 1875252   Mean   : 15.38   Mean   :4.517e+11   Mean   : 16850  
 3rd Qu.: 1479941   3rd Qu.: 20.00   3rd Qu.:2.627e+11   3rd Qu.: 24922  
 Max.   :43805214   Max.   :224.97   Max.   :1.812e+13   Max.   :126352  
                                                                         
           generation      continent       life_exp        avg_temp          max_temp     
 G.I. Generation:2724   Africa  : 690   Min.   :53.98   Min.   :-18.085   Min.   :-2.065  
 Silent         :6262   Americas:7630   1st Qu.:70.89   1st Qu.:  9.469   1st Qu.:19.398  
 Boomers        :4908   Asia    :4290   Median :74.57   Median : 14.165   Median :24.004  
 Generation X   :5688   Europe  :9390   Mean   :74.10   Mean   : 15.888   Mean   :23.383  
 Millenials     :3228   Oceania : 810   3rd Qu.:77.74   3rd Qu.: 25.743   3rd Qu.:27.966  
 Generation Z   :   0                   Max.   :83.79   Max.   : 29.671   Max.   :38.842  
                                                                                          
    min_temp       new_suicides_no   new_suicide_ratio   log_population    log_GDP_year  
 Min.   :-33.783   Min.   :    1.0   Min.   :5.830e-07   Min.   : 5.628   Min.   :17.66  
 1st Qu.: -2.155   1st Qu.:    7.0   1st Qu.:4.955e-05   1st Qu.:11.489   1st Qu.:22.94  
 Median :  5.740   Median :   43.0   Median :1.101e-04   Median :12.975   Median :24.62  
 Mean   :  7.952   Mean   :  293.9   Mean   :1.852e-04   Mean   :12.790   Mean   :24.56  
 3rd Qu.: 22.701   3rd Qu.:  181.0   3rd Qu.:2.296e-04   3rd Qu.:14.208   3rd Qu.:26.29  
 Max.   : 27.492   Max.   :22339.0   Max.   :3.597e-03   Max.   :17.595   Max.   :30.53  
                                                                                         
 log_GDP_capita   log_suicide_no   log_suicide_ratio sqrt_population   sqrt_GDP_year    
 Min.   : 5.525   Min.   : 0.000   Min.   :-14.354   Min.   :  16.67   Min.   :   6850  
 1st Qu.: 8.139   1st Qu.: 1.946   1st Qu.: -9.912   1st Qu.: 312.51   1st Qu.:  95966  
 Median : 9.146   Median : 3.761   Median : -9.114   Median : 656.77   Median : 221832  
 Mean   : 9.063   Mean   : 3.613   Mean   : -9.177   Mean   : 971.62   Mean   : 411724  
 3rd Qu.:10.124   3rd Qu.: 5.198   3rd Qu.: -8.379   3rd Qu.:1216.53   3rd Qu.: 512552  
 Max.   :11.747   Max.   :10.014   Max.   : -5.628   Max.   :6618.55   Max.   :4256843  
                                                                                        
 sqrt_GDP_capita  sqrt_suicide_no   sqrt_suicide_ratio
 Min.   : 15.84   Min.   :  0.000   Min.   : 0.000    
 1st Qu.: 58.52   1st Qu.:  2.449   1st Qu.: 1.729    
 Median : 96.84   Median :  6.481   Median : 2.929    
 Mean   :111.94   Mean   : 10.602   Mean   : 3.227    
 3rd Qu.:157.87   3rd Qu.: 13.416   3rd Qu.: 4.472    
 Max.   :355.46   Max.   :149.459   Max.   :14.999    
                                                      
summary(suicide_outliers)
               country          year          sex          age       suicides_no     
 Russian Federation: 109   Min.   :1985   Female:  53   5-14 :  0   Min.   :    1.0  
 Kazakhstan        :  99   1st Qu.:1995   Male  :1570   15-24: 44   1st Qu.:   72.0  
 Lithuania         :  94   Median :2001                 25-34:171   Median :  204.0  
 Ukraine           :  88   Mean   :2001                 35-54:255   Mean   : 1107.4  
 Hungary           :  86   3rd Qu.:2007                 55-74:336   3rd Qu.:  859.5  
 Belarus           :  78   Max.   :2015                 75+  :817   Max.   :22338.0  
 (Other)           :1069                                                             
   population       suicide_ratio     GDP_for_year       GDP_per_capita  
 Min.   :     889   Min.   : 45.52   Min.   :4.692e+07   Min.   :   425  
 1st Qu.:  103700   1st Qu.: 54.26   1st Qu.:1.654e+10   1st Qu.:  2891  
 Median :  273776   Median : 66.90   Median :4.552e+10   Median :  7458  
 Mean   : 1636117   Mean   : 72.65   Mean   :3.840e+11   Mean   : 13201  
 3rd Qu.: 1274659   3rd Qu.: 84.08   3rd Qu.:1.959e+11   3rd Qu.: 19012  
 Max.   :21476420   Max.   :224.97   Max.   :8.100e+12   Max.   :113120  
                                                                         
           generation     continent       life_exp        avg_temp          max_temp     
 G.I. Generation:447   Africa  :   8   Min.   :61.42   Min.   :-18.085   Min.   :-2.065  
 Silent         :690   Americas: 229   1st Qu.:69.01   1st Qu.:  6.761   1st Qu.:18.369  
 Boomers        :265   Asia    : 298   Median :72.43   Median :  9.377   Median :21.479  
 Generation X   :187   Europe  :1080   Mean   :72.66   Mean   : 10.965   Mean   :21.537  
 Millenials     : 34   Oceania :   8   3rd Qu.:76.20   3rd Qu.: 13.192   3rd Qu.:24.358  
 Generation Z   :  0                   Max.   :83.20   Max.   : 28.849   Max.   :36.894  
                                                                                         
    min_temp        new_suicides_no   new_suicide_ratio   log_population   log_GDP_year  
 Min.   :-33.7830   Min.   :    2.0   Min.   :0.0004552   Min.   : 6.79   Min.   :17.66  
 1st Qu.: -7.6490   1st Qu.:   73.0   1st Qu.:0.0005556   1st Qu.:11.55   1st Qu.:23.53  
 Median : -2.5030   Median :  205.0   Median :0.0006902   Median :12.52   Median :24.54  
 Mean   : -0.3596   Mean   : 1108.4   Mean   :0.0007523   Mean   :12.68   Mean   :24.72  
 3rd Qu.:  2.5870   3rd Qu.:  860.5   3rd Qu.:0.0008599   3rd Qu.:14.06   3rd Qu.:26.00  
 Max.   : 27.2080   Max.   :22339.0   Max.   :0.0033746   Max.   :16.88   Max.   :29.72  
                                                                                         
 log_GDP_capita   log_suicide_no    log_suicide_ratio sqrt_population   sqrt_GDP_year    
 Min.   : 6.052   Min.   : 0.6931   Min.   :-7.695    Min.   :  29.82   Min.   :   6850  
 1st Qu.: 7.969   1st Qu.: 4.2905   1st Qu.:-7.495    1st Qu.: 322.02   1st Qu.: 128594  
 Median : 8.917   Median : 5.3230   Median :-7.278    Median : 523.24   Median : 213353  
 Mean   : 8.866   Mean   : 5.4378   Mean   :-7.244    Mean   : 888.36   Mean   : 395976  
 3rd Qu.: 9.853   3rd Qu.: 6.7575   3rd Qu.:-7.059    3rd Qu.:1129.01   3rd Qu.: 442612  
 Max.   :11.636   Max.   :10.0141   Max.   :-5.691    Max.   :4634.27   Max.   :2846085  
                                                                                         
 sqrt_GDP_capita  sqrt_suicide_no   sqrt_suicide_ratio
 Min.   : 20.62   Min.   :  1.000   Min.   : 6.747    
 1st Qu.: 53.77   1st Qu.:  8.485   1st Qu.: 7.366    
 Median : 86.36   Median : 14.283   Median : 8.179    
 Mean   : 99.74   Mean   : 23.140   Mean   : 8.423    
 3rd Qu.:137.88   3rd Qu.: 29.317   3rd Qu.: 9.169    
 Max.   :336.33   Max.   :149.459   Max.   :14.999    
                                                      

The analysis shows that the top six countries with outlier suicide ratios are the Russian Federation, Kazakhstan, Ukraine, Lithuania, Hungary, and Belarus. Intriguingly, these nations are not only geographically proximate, but also share cultural and historical links. This observation may imply potential regional trends or shared socio-economic factors influencing the elevated suicide ratios.

Furthermore, a striking detail emerges from the outliers: nearly all, or 96.7%, are men. This finding indicates a significantly higher incidence of extreme suicide ratios among men.

dim(suicide_outliers)
[1] 1623   27

Due to the nature of our data, and the analysis we performed we believe that removing outliers or applying trunication(Winsorization) will cause information loss. so we keep the outliers for EDA.and we will try different methods on modeling the data to see which perform the best on our data. these are the methods we will try: 1. removing detected outliers with both Tukey’s Fence and Z_score method. With removing outliers However, this can be risky because it assumes that the outliers are not informative and may lead to biased estimates.

  1. Robust Regression Methods: Given the number of outliers and their potential influence on the model, a robust regression method might be a good choice. These methods are less sensitive to outliers and can often provide better predictive performance when outliers are present.

  2. Other Machine learning models which are less sensetive to outliers. (to be edited )

3. Explore Data

In this forthcoming section, we dive into the exploration of our dataset, distinguishing variables into four distinct categories.

Firstly, we have ‘year’ which falls under time-dependent variables, mapping the temporal evolution of our data.

Secondly, we have a set of geographical and meteorological variables. These include ‘continent’, ‘country’, ‘population’, and a range of temperature parameters (minimum, maximum, average) alongside their transformations, offering us insights into regional and environmental influences.

Our third category brings together social and economic variables such as ‘life expectancy’, ‘GDP’, and ‘GDP per capita’. These, along with their respective transformations, capture the socio-economic backdrop against which we observe our data.

Lastly, our fourth category comprises demographic variables, namely ‘sex’ and ‘age’, allowing us to examine the influence of these vital demographics on our data.

Moreover, we have identified three potential target variables for our study: ‘suicide_ratio’, ‘log_suicide_ratio’, and ‘sqrt_suicide_ratio’. Of these, ‘log_suicide_ratio’ has been found to be highly effective in minimizing the impact of outliers. Yet, our exploration won’t be limited to it. We aim to thoroughly investigate the impact of all variables on each potential target until we embark on the modeling phase, where we will select the most suitable target variable for our predictive model.

glimpse(data)
Rows: 22,810
Columns: 27
$ country            <fct> Albania, Albania, Albania, Albania, Albania, Albania, Albania, …
$ year               <int> 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 198…
$ sex                <fct> Male, Male, Female, Male, Male, Female, Female, Female, Male, F…
$ age                <ord> 15-24, 35-54, 15-24, 75+, 25-34, 75+, 35-54, 25-34, 55-74, 55-7…
$ suicides_no        <dbl> 21, 16, 14, 1, 9, 1, 6, 4, 1, 0, 2, 17, 1, 14, 4, 8, 3, 5, 5, 4…
$ population         <dbl> 312900, 308000, 289700, 21800, 274300, 35600, 278800, 257200, 1…
$ suicide_ratio      <dbl> 6.71, 5.19, 4.83, 4.59, 3.28, 2.81, 2.15, 1.56, 0.73, 0.00, 5.4…
$ GDP_for_year       <dbl> 2156624900, 2156624900, 2156624900, 2156624900, 2156624900, 215…
$ GDP_per_capita     <dbl> 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 769, 769, 769…
$ generation         <ord> Generation X, Silent, Generation X, G.I. Generation, Boomers, G…
$ continent          <fct> Europe, Europe, Europe, Europe, Europe, Europe, Europe, Europe,…
$ life_exp           <dbl> 72.352, 72.352, 72.352, 72.352, 72.352, 72.352, 72.352, 72.352,…
$ avg_temp           <dbl> 12.99658, 12.99658, 12.99658, 12.99658, 12.99658, 12.99658, 12.…
$ max_temp           <dbl> 24.084, 24.084, 24.084, 24.084, 24.084, 24.084, 24.084, 24.084,…
$ min_temp           <dbl> 3.304, 3.304, 3.304, 3.304, 3.304, 3.304, 3.304, 3.304, 3.304, …
$ new_suicides_no    <dbl> 22, 17, 15, 2, 10, 2, 7, 5, 2, 1, 3, 18, 2, 15, 5, 9, 4, 6, 6, …
$ new_suicide_ratio  <dbl> 7.031000e-05, 5.519481e-05, 5.177770e-05, 9.174312e-05, 3.64564…
$ log_population     <dbl> 12.653639, 12.637855, 12.576601, 9.989665, 12.521978, 10.480101…
$ log_GDP_year       <dbl> 21.49181, 21.49181, 21.49181, 21.49181, 21.49181, 21.49181, 21.…
$ log_GDP_capita     <dbl> 6.679599, 6.679599, 6.679599, 6.679599, 6.679599, 6.679599, 6.6…
$ log_suicide_no     <dbl> 3.0910425, 2.8332133, 2.7080502, 0.6931472, 2.3025851, 0.693147…
$ log_suicide_ratio  <dbl> -9.562596, -9.804642, -9.868551, -9.296518, -10.219393, -9.7869…
$ sqrt_population    <dbl> 559.3747, 554.9775, 538.2379, 147.6482, 523.7366, 188.6796, 528…
$ sqrt_GDP_year      <dbl> 46439.48, 46439.48, 46439.48, 46439.48, 46439.48, 46439.48, 464…
$ sqrt_GDP_capita    <dbl> 28.21347, 28.21347, 28.21347, 28.21347, 28.21347, 28.21347, 28.…
$ sqrt_suicide_no    <dbl> 4.582576, 4.000000, 3.741657, 1.000000, 3.000000, 1.000000, 2.4…
$ sqrt_suicide_ratio <dbl> 2.5903668, 2.2781571, 2.1977261, 2.1424285, 1.8110770, 1.676305…
column_name <- colnames(data)

3.1 Time-Dependent

# the global rate over the time period will be useful:
global_average <- (sum(as.numeric(data$suicides_no)) / sum(as.numeric(data$population))) * 100000

data %>%
  group_by(year) %>%
  summarize(population = sum(population), 
            suicides = sum(suicides_no), 
            suicides_per_100k = (suicides / population) * 100000) %>%
  ggplot(aes(x = year, y = suicides_per_100k)) + 
  geom_line(col = "red", linewidth = 1) + 
  geom_point(col = "red", size = 2) + 
  geom_hline(yintercept = global_average, linetype = 2, color = "grey35", linewidth = 1) +
  labs(title = "Global Suicides (per 100k)",
       subtitle = "Trend over time, 1985 - 2015.",
       x = "Year", 
       y = "Suicides per 100k") + 
  scale_x_continuous(breaks = seq(1985, 2015, 2)) + 
  scale_y_continuous(breaks = seq(10, 20))

The plot above yields several insightful observations:

  • The highest suicide rate recorded was 18.7 deaths per 100k population, observed in 1995.
  • This rate has seen a consistent decrease, falling to 13.5 per 100k population by 2015, which translates to a significant reduction of about 27%.
  • Presently, the rates are gradually regressing towards the figures prevalent prior to the 1990s.

However, a crucial aspect to remember is that the data available from the 1980s is relatively scarce, thus making it difficult to conclusively state whether these rates were an accurate reflection of the global suicide trends during that period.

3.1.1 Why did people killed themselves in 1995?

data_95 <- data %>%
  filter(year == 1995) %>%
  group_by(country) %>%
  summarize(population = sum(population), 
            suicides = sum(suicides_no), 
            suicides_per_100k = (suicides / population) * 100000)

data_95 <- data_95 %>%
  arrange(desc(suicides_per_100k))

head(data_95)

During our exploration of outliers, we observed that Eastern European countries have significantly higher suicide rates compared to other nations. In particular, it appears that a substantial number of suicides in 1995 were reported in Russia.

data_without_ru <- data %>%
  filter(country != "Russian Federation") %>%
  group_by(year) %>%
  summarize(population = sum(population), 
            suicides = sum(suicides_no), 
            suicides_per_100k = (suicides / population) * 100000)


yearly_data <- data %>%
  group_by(year) %>%
  summarize(population = sum(population), 
            suicides = sum(suicides_no), 
            suicides_per_100k = (suicides / population) * 100000)

data_without_ru <- data_without_ru %>%
  mutate(inclusion = "Without Russia")

yearly_data <- yearly_data %>%
  mutate(inclusion = "With Russia")

combined_data <- bind_rows(data_without_ru, yearly_data)

ggplot(combined_data, aes(x = year, y = suicides_per_100k, color = inclusion)) +
  geom_line() +
  labs(
    title = 'Number of Suicides for every 100k people', 
    x = 'Year', 
    y = 'Suicide Ratio per 100k',
    color = "Country Inclusion"
  ) +
  theme(legend.position = "right") +
  scale_color_manual(values = c("Without Russia" = "blue", "With Russia" = "red"))

The significant increase in suicide rates observed in Russia in 1995 can be attributed to a combination of several factors, each contributing to an overall sense of despair and instability within the population:

Economic Crisis: The period marked Russia’s challenging transition from a centrally planned economy to a free-market system. This shift resulted in substantial economic turmoil characterized by high unemployment rates, rampant inflation, and general economic uncertainty. As numerous studies have indicated, such economic hardships can greatly increase stress levels within the population, thereby leading to higher suicide rates.

Political Instability: The dissolution of the Soviet Union in 1991 precipitated a series of radical political and societal changes. The ensuing uncertainty and the resultant feeling of insecurity may have exacerbated the already volatile situation, thereby contributing to the rise in suicide rates.

Rise in Alcoholism: During this period, Russia experienced an increase in alcohol abuse, a problem that has historically been a challenge for the country. A well-established body of research shows a strong correlation between alcohol abuse and suicide rates. The spike in alcoholism during this time might have, therefore, been a significant contributor to the suicide rates links.

It is essential to note that while these factors are distinct, they are interrelated and likely exacerbated each other’s effects on the population’s mental health.

3.2 Geographical and Meteorological

3.2.1 Population

3.2.1.1 Univariate Analysis

data$scaled_population <- (data$population - min(data$population))/(max(data$population)-min(data$population))
data$scaled_log_population <- (data$log_population - min(data$log_population)) / (max(data$log_population) - min(data$log_population))
library(gridExtra)

# Original Population
p1 <- ggplot(data, aes(population)) +
  geom_histogram(binwidth=1000, fill="skyblue", color="black") +
  labs(title = "Histogram of Population",
       x = "",
       y = "Count")

p2 <- ggplot(data, aes(x = "", y = population)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of Population",
       x = "",
       y = "")

# Scaled Population
p3 <- ggplot(data, aes(scaled_population)) +
  geom_histogram(binwidth=0.01, fill="skyblue", color="black") +
  labs(title = "Histogram of Scaled Population",
       x = "",
       y = "Count")

p4 <- ggplot(data, aes(x = "", y = scaled_population)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of Scaled Population",
       x = "",
       y = "")

# Scaled Log Population
p5 <- ggplot(data, aes(scaled_log_population)) +
  geom_histogram(binwidth=0.01, fill="skyblue", color="black") +
  labs(title = "Histogram of Scaled Log Population",
       x = "",
       y = "Count")

p6 <- ggplot(data, aes(x = "", y = scaled_log_population)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of Scaled Log Population",
       x = "",
       y = "")

# Arrange the plots in a grid
grid.arrange(p1, p2, p3, p4, p5, p6, ncol = 2)

From these visualizations, we can observe that the distribution of the population variable becomes significantly less skewed after applying both min-max scaling and a log transformation. The resulting scaled log population appears more normally distributed and shows fewer outliers compared to the original and scaled population. Therefore, using the ‘scaled_log_population’ variable in our analyses should yield more robust results.

3.2.1.2 Bivariate Analysis

In this section, we investigate the correlation between our candidate target variables and scaled_log_population. Since our suicide_ratio variable was derived from the ratio of suicide_no to population, our analysis focuses on the relationship between scaled_log_population and the three transformations of suicide_no: namely, suicide_no, log_suicide_no, and sqrt_suicide_no.

data_long <- data %>%
  select(scaled_log_population, suicides_no, log_suicide_no, sqrt_suicide_no) %>%
  pivot_longer(cols = -scaled_log_population, names_to = "variable", values_to = "value")

ggplot(data_long, aes(x = scaled_log_population, y = value, color= "skyblue")) +
  geom_point(size=0.2) +
  facet_wrap(~variable, scales = "free_y") +
  labs(x = "Scaled Log Population",
       y = "Value",
       title = "Scatterplot of Scaled Log Population vs. Target Variables") + 
  theme(legend.position = "none") 

From these scatterplots, it appears that the relationship between scaled_log_population and log_suicide_no is more linear compared to the other variables.

# List of variables to calculate correlations with scaled_log_population
vars <- c("suicides_no", "log_suicide_no", "sqrt_suicide_no")

# Calculate correlations
correlations <- purrr::map_dbl(vars, ~cor(data$scaled_log_population, data[[.]]))

# Print correlations
names(correlations) <- vars
print(correlations)
    suicides_no  log_suicide_no sqrt_suicide_no 
      0.3930255       0.8622564       0.6715714 

Is it surprising that there is a high correlation between the transformation of population and suicide_no? Not at all!

Our goal is to estimate the probability of an individual taking their own life, which we calculate by dividing the suicide_no by the population in each row. However, this means we cannot directly examine the relationship between suicide_ratio and population, as we use the population directly to calculate the ratio. Instead, we need to analyze the relationship between population and suicide_no for each row.

When the population of each row increases, the probability of a higher suicide_no also tends to increase. However, this comparison may not be entirely accurate. Should we disregard population altogether? Before making that decision, let’s address this question: Is there a relationship between a country’s population and suicide_ratio? In other words, is the probability of an individual taking their own life higher in countries with a larger population?

To explore this, we have stratified the population of each country into three categories (big, medium, and small) for each year. This allows us to examine whether there is a notable difference in suicide_ratio based on the size of the population.

Before moving forward, let’s create a dataframe that calculates the population of each country over the years.

pop_data <- data %>%
  group_by(year, country) %>%
  summarize(population = sum(population, na.rm = TRUE), .groups = "drop")

We aim to establish thresholds for each year to classify countries into four categories: very small, small, medium, and large. It is important to identify natural gaps in the distribution of population for each country within a given year. It should be noted that a median-based approach is not suitable since it would result in three categories of equal size. Additionally, we need to compare the population of each country with the populations of other countries within the same year, as a country may not be densely populated at present but could experience a significant increase in population in the future.

To accomplish this, we employ the Jenks natural breaks classification method for each year. This method aims to minimize the variance within classes while maximizing the variance between classes. It involves an iterative process that reallocates observations from one class to another until an optimal arrangement is achieved.

# Load required library
library(classInt)

# Define a function to apply Fisher-Jenks method for binning
fisher_jenks <- function(x) {
  bins <- classIntervals(x, n = 5, style = "fisher")$brks
  cut(x, breaks = bins, labels = c("Very_Small","Small", "Medium", "Large","Very_Large" ), include.lowest = TRUE)
}

# Add the new column to the data frame
pop_data <- pop_data %>%
  group_by(year) %>%
  mutate(population_bine_jenks = fisher_jenks(population))

# View the first few rows of the new data
head(pop_data)

Now, let’s examine the population categories we’ve created:

# Tabulate the categories
pop_distribution <- table(pop_data$population_bine_jenks)
print(pop_distribution)

Very_Small      Small     Medium      Large Very_Large 
      1714        249        202         85         31 

From the output, we notice that the category “Very Large” consists of only one country, the USA. To have more balanced categories, we’ll combine the USA with the “Large” category and rename accordingly:

# Adjust the categories
pop_data <- pop_data %>%
  mutate(population_bine_jenks = ifelse(population_bine_jenks == "Very_Large", "Large",
                                        as.character(population_bine_jenks)))

# Check the distribution after adjustment
adjusted_pop_distribution <- table(pop_data$population_bine_jenks)
print(adjusted_pop_distribution)

     Large     Medium      Small Very_Small 
       116        202        249       1714 
pop_data
# Join the population categories into our original data
data<- data %>%
  left_join(pop_data%>%
  select(country,year,population_bine_jenks),by = c("year", "country"))

We also bin the population based on the median and compare its performance with the Jenks natural breaks classification method in our model.

data_sum <- data %>%
  group_by(country, year) %>%
  summarise(population = sum(population))
`summarise()` has grouped output by 'country'. You can override using the `.groups` argument.
thresholds <- quantile(data_sum$population, probs = c(0.25, 0.5, 0.75))

# Assign each country-year pair to a population category
data_binned <- data_sum %>%
  mutate(
    population_bine_median = case_when(
      population <= thresholds[1] ~ "Very_Small",
      population > thresholds[1] & population <= thresholds[2] ~ "Small",
      population > thresholds[2] & population <= thresholds[3] ~ "Medium",
      TRUE ~ "Large"
    )
  )

data <- data %>%
  left_join(data_binned%>%
  select(country,year,population_bine_median),by = c("year", "country"))

In this step, we will explore the relationship between log_suicide_ratio (chosen because it follows a normal distribution) and population_binde_jenks.

Let’s start by examining the descriptive statistics for both log_suicide_ratio and suicide_ratio.

data %>%
  group_by(population_bine_jenks) %>%
  summarise(
    mean_suicide_ratio = mean(log_suicide_ratio, na.rm = TRUE),
    median_suicide_ratio = median(log_suicide_ratio, na.rm = TRUE),
    min_suicide_ratio = min(log_suicide_ratio, na.rm = TRUE),
    max_suicide_ratio = max(log_suicide_ratio, na.rm = TRUE)
  )
data_grouped <- data %>%
  group_by(population_bine_jenks) %>%
  summarise(
    suicide_ratio = sum(suicides_no) / (sum(population) )
  )

ggplot(data_grouped, aes(x = population_bine_jenks, y = suicide_ratio, fill = population_bine_jenks)) +
  geom_bar(stat = "identity", color = "black") +
  labs(x = "Population Bin", y = "Suicide Ratio", title = "Suicide Ratio for each Population Bin") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5), legend.position = "none") +
  scale_fill_brewer(palette = "Set2")

The above plot suggests that the suicide ratio in Large countires is much higher. Now we use statistical tests to see if this difference is statisticaly siginifticant. Since we have more than two samples we can use ANOVA. It’s important to keep in mind that ANOVA makes several assumptions, including that the residuals are normally distributed, that the variances are equal across groups, and that the observations are independent. We should check these assumptions before interpreting the results.

# Fit the model
pop_anova <- aov(log_suicide_ratio ~ population_bine_jenks, data = data)

# Run the ANOVA
anova_result <- anova(pop_anova)

# Print the result
print(anova_result)
Analysis of Variance Table

Response: log_suicide_ratio
                         Df  Sum Sq Mean Sq F value    Pr(>F)    
population_bine_jenks     3   486.3  162.10  126.64 < 2.2e-16 ***
Residuals             22806 29191.4    1.28                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Testing Normality of Residuals Assumption for ANOVA:

# Create a data frame for residuals
residuals_df <- data.frame(residuals = residuals(pop_anova))

# Create histogram of residuals
hist_plot <- ggplot(residuals_df, aes(x = residuals)) +
  geom_histogram(fill = 'steelblue', color = 'black', bins = 30) +
  theme_minimal() +
  labs(x = "Residuals", y = "Frequency",
       title = "Histogram of Residuals")

# Create Q-Q plot of residuals
qq_plot <- ggplot(residuals_df, aes(sample = residuals)) +
  geom_qq(color = 'steelblue') +
  geom_qq_line(color = 'red') +
  theme_minimal() +
  labs(title = "Normal Q-Q Plot",
       x = "Theoretical Quantiles",
       y = "Sample Quantiles")

# Arrange the plots side by side using the gridExtra package
library(gridExtra)
grid.arrange(hist_plot, qq_plot, ncol = 2)

Testing Homogeneity of Variances Assumption for ANOVA:

leveneTest(log_suicide_ratio ~ age, data = data)
Levene's Test for Homogeneity of Variance (center = median)
         Df F value    Pr(>F)    
group     4  55.038 < 2.2e-16 ***
      22805                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
bartlett.test(log_suicide_ratio ~ age, data = data)

    Bartlett test of homogeneity of variances

data:  log_suicide_ratio by age
Bartlett's K-squared = 250.28, df = 4, p-value < 2.2e-16

The small p-values in our tests lead us to reject the null hypothesis, which posits that the variances are equivalent across different groups. This implies that the results from an ANOVA test may not be reliable in our case. Therefore, we turn to the Kruskal-Wallis test.

The Kruskal-Wallis test is a non-parametric technique that assesses whether samples originate from the same distribution. It is suitable for comparing two or more independent samples of equal or differing sample sizes, effectively extending the Mann-Whitney U test, which is utilized for comparing only two groups.

Unlike the one-way ANOVA and t-tests, the Kruskal-Wallis test does not require the residuals to be normally distributed. Consequently, it can be used with continuous data that doesn’t follow a normal distribution. However, akin to ANOVA, it evaluates whether the mean ranks of the groups are different, not the means themselves.

The assumptions for the Kruskal-Wallis test are:

Independence: Observations within and between each sample should be independent, implying that one observation’s presence or absence doesn’t influence another observation’s presence or absence.

Ordinal Data: The dependent variable must be ordinal at a minimum, meaning that the observations can be ordered. It should be possible to say that one observation is greater than, equal to, or less than another observation.

Shape of Distribution: Although the Kruskal-Wallis test doesn’t require a specific data distribution like ANOVA does, it assumes that the shape of the distribution is identical for each group. While groups may have differing medians, their distribution’s overall shape should be the same.

For our data, we assume independence. Our ‘population_bin’ variable is ordinal, thereby fulfilling the second assumption. To verify the third assumption, we need to evaluate the distribution shapes via plots.

# Histogram
ggplot(data, aes(x =log_suicide_ratio)) +
  geom_histogram(binwidth = 1) +
  facet_wrap(~ population_bine_jenks)


# Density plot
ggplot(data, aes(x = log_suicide_ratio)) +
  geom_density() +
  facet_wrap(~ population_bine_jenks)


# Q-Q plot
ggplot(data, aes(sample = log_suicide_ratio)) +
  stat_qq() +
  facet_wrap(~ population_bine_jenks)

kruskal.test(log_suicide_ratio ~ population_bine_jenks, data = data)

    Kruskal-Wallis rank sum test

data:  log_suicide_ratio by population_bine_jenks
Kruskal-Wallis chi-squared = 347.32, df = 3, p-value < 2.2e-16

The result of the Kruskal-Wallis test aligns with that of the ANOVA. The obtained p-value is significantly small, indicating that the suicide rate differs significantly among different population categories.

#Factorizing our two new column population_bine_jenks and population_bine_median

data$population_bine_jenks <- factor(data$population_bine_jenks, 
                   ordered = T, 
                   levels = c("Very_Small",
                              "Small",
                              "Medium",
                              "Large"))
data$population_bine_median <- factor(data$population_bine_median, 
                   ordered = T, 
                   levels = c("Very_Small",
                              "Small",
                              "Medium",
                              "Large"))

3.2.2 Country and Continent

3.2.2.1 Univariate Analysis

# Count the number of observations for each country
country_counts <- data %>% 
  group_by(country) %>% 
  summarise(count = n()) %>% 
  arrange(desc(count))

# Create the bar plot
ggplot(country_counts, aes(x = reorder(country, count), y = count)) +
  geom_bar(stat = "identity", fill = "steelblue", color = "black") +
  labs(x = "Country", y = "Number of Rows", title = "Number of Rows for Each Country") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 15), 
        axis.title.y = element_text(size = 10),
        axis.title.x = element_text(size = 10),
        axis.text.x = element_blank()) # Remove country names

# Join the data to a map
map_data <- joinCountryData2Map(country_counts, joinCode = "NAME", nameJoinColumn = "country")
91 codes from your data successfully matched countries in the map
1 codes from your data failed to match with a country code in the map
152 codes from the map weren't represented in your data
# Set margins
par(mar = c(0, 0, 1, 0))

# Plot the map
mapCountryData(map_data, 
               nameColumnToPlot = "count", 
               mapTitle = "Number of Rows for each Country", 
               colourPalette = "blues",  # change color palette here
               oceanCol = "lightblue", 
               missingCountryCol = "grey65", 
               catMethod = "pretty")
You asked for 7 categories, 6 were used due to pretty() classification
Warning: colourPalette should be set to either a vector of colours or one of :white2Black black2White palette heat topo terrain rainbow negpos8 negpos9 
setting to heat colours as default

# Get the unique countries per continent in the data
continent_count <- data %>%
  group_by(continent) %>%
  summarise(num_countries_data = n_distinct(country))

# Actual country count per continent
actual_count <- data.frame(
  continent = c("Africa", "Asia", "Europe", "Americas", "Oceania"),
  num_countries_actual = c(54, 48, 44, 35, 14)  # replace with actual numbers
)

# Merge the two data frames
continent_count <- merge(continent_count, actual_count, by = "continent")

# Convert to long format for plotting
continent_count_long <- continent_count %>%
  pivot_longer(cols = -continent, 
               names_to = "category", 
               values_to = "count")

# Create a bar plot
# Then, create a bar plot
ggplot(continent_count_long, aes(x = continent, y = count, fill = category)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(x = "Continent", y = "Number of Countries",
       title = "Actual Countries vs Countries in Data per Continent") +
  scale_fill_discrete(name = "", labels = c("Number of Countries in Our Data", "Actual Number of Countries")) +
  theme_minimal()

The presented plots reveal that we have a limited number of samples from Africa, which should be taken into consideration when interpreting the results for this region. Additionally, it is important to note that our data for Asia is also relatively sparse, which may impact the robustness of any conclusions drawn for this continent.

3.2.2.2 Bivariate Analysis

country <- data %>%
  group_by(country, continent) %>%
  summarize(n = n(), 
            suicide_per_100k = (sum(as.numeric(suicides_no)) / sum(as.numeric(population))) * 100000,
            .groups="drop") %>%
  arrange(desc(suicide_per_100k))

country$country <- factor(country$country, 
                          ordered = T, 
                          levels = rev(country$country))

ggplot(country, aes(x = country, y = suicide_per_100k, fill = continent)) + 
  geom_bar(stat = "identity") + 
  geom_hline(yintercept = global_average, linetype = 2, color = "grey35", size = 1) +
  labs(title = "Global suicides per 100k, by Country",
       x = "Country", 
       y = "Suicides per 100k", 
       fill = "Continent") +
  #coord_flip() +
  scale_y_continuous(breaks = seq(0, 45, 2)) + 
  theme(
  legend.position = "top",
  legend.key.size = unit(0.25, "cm"),
  plot.title = element_text(hjust = 0.5),
  axis.text.x = element_text(angle = 90, hjust = 0.5, vjust = 1, size= 4),
  #axis.line.x = element_line(inherit.blank = TRUE)
  ) 
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

Lithuania’s rate has been highest by a large margin: > 44 suicides per 100k (per year)

country <- data %>%
  group_by(country) %>%
  summarize(suicide_per_100k = (sum(as.numeric(suicides_no)) / sum(as.numeric(population))) * 100000,
            .groups = "drop")

countrydata <- joinCountryData2Map(country, joinCode = "NAME", nameJoinColumn = "country")
91 codes from your data successfully matched countries in the map
1 codes from your data failed to match with a country code in the map
152 codes from the map weren't represented in your data
par(mar=c(0, 0, 2, 0)) # margins

mapCountryData(countrydata, 
nameColumnToPlot="suicide_per_100k", 
mapTitle="Sucide per 100k across the Globe", 
colourPalette = "heat", 
oceanCol="lightblue", 
missingCountryCol="grey65", 
catMethod = "pretty")
You asked for 7 categories, 10 were used due to pretty() classification

mapCountryData(countrydata, 
nameColumnToPlot="suicide_per_100k", 
mapTitle="Suicides per 100k in Eurasia", 
mapRegion = "eurasia", 
colourPalette = "heat", 
oceanCol="lightblue", 
missingCountryCol="grey65", 
addLegend = FALSE, 
catMethod = "pretty")
You asked for 7 categories, 10 were used due to pretty() classification

It’s essential to be aware of our data’s limitations. Specifically, we’re missing a significant amount of information for Africa and Asia. On top of that, eight countries were excluded due to lack of sufficient data.

Therefore, our analyses, whether on a global or continent level, might not provide a fully accurate picture. We’re essentially trying to make sense of a puzzle with missing pieces.

Lastly, when comparing suicide rates between different countries, it’s crucial to consider that what is recorded as a suicide can vary by country. The reliability of suicide reporting can also influence our results.

So, even though our analysis can help identify some trends, we must keep these caveats in mind when interpreting our findings.

Due to the limited availability of data from Africa, we have excluded this continent from the current analysis.


data_continent <- data %>% filter(continent != "Africa")

# Function to calculate suicide rate per 100k
calculate_suicide_rate <- function(suicides_no, population) {
  (sum(as.numeric(suicides_no)) / sum(as.numeric(population))) * 100000
}

# Calculating suicide rates by continent
continent <-data_continent %>%
  group_by(continent) %>%
  summarize(suicide_per_100k = calculate_suicide_rate(suicides_no, population)) %>%
  arrange(suicide_per_100k)


continent_plot <- ggplot(continent, aes(x = continent, y = suicide_per_100k, fill = continent)) + 
  geom_bar(stat = "identity") + 
  labs(title = "Suicide Rates per 100k by Continent",
       x = "Continent", 
       y = "Suicides per 100k", 
       fill = "Continent") +
  theme(legend.position = "none") + 
  scale_y_continuous(breaks = seq(0, 20, 1), minor_breaks = F)
print(continent_plot)

Our preliminary analysis suggests that Europe appears to have a higher suicide rate compared to other continents. To further substantiate this observation, we should validate it with rigorous statistical tests. After all, our dataset is merely a sample and doesn’t necessarily represent the whole population.

Hence, we need to investigate if the observed differences are statistically significant.

Our null hypothesis (H0) states that the mean suicide rates are identical across all continents over the 30-year span.

On the other hand, our alternative hypothesis (H1) asserts that at least one continent has a distinct mean suicide rate compared to the others.

We set our significance level at 0.05 for these tests.

For comparison, we’ll initially employ the ANOVA test. If our data does not satisfy the assumptions required for ANOVA, we’ll resort to the non-parametric Kruskal-Wallis test.

continent_anova <- aov(log_suicide_ratio ~ continent, data = data_continent)

# Run the ANOVA
anova_result <- anova(continent_anova)

# Print the result
print(anova_result)
Analysis of Variance Table

Response: log_suicide_ratio
             Df  Sum Sq Mean Sq F value    Pr(>F)    
continent     3  1098.9  366.30  302.69 < 2.2e-16 ***
Residuals 22116 26763.3    1.21                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
kruskal.test(log_suicide_ratio ~ continent, data = data_continent)

    Kruskal-Wallis rank sum test

data:  log_suicide_ratio by continent
Kruskal-Wallis chi-squared = 801.43, df = 3, p-value < 2.2e-16

The p-values derived from both tests are significantly small, leading us to reject the null hypothesis. Thus, we can infer that at least one continent has a distinctive suicide rate. However, the ANOVA test does not provide insights about which specific continent diverges, nor does it indicate the extent of this difference.

To delve into these specifics, we’ll utilize the Tukey’s Honest Significant Difference (HSD) post-hoc test. This test will facilitate the identification of groups with significantly different means, providing a comprehensive understanding of our data.

TukeyHSD(continent_anova)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = log_suicide_ratio ~ continent, data = data_continent)

$continent
                        diff         lwr         upr     p adj
Asia-Americas    -0.14199557 -0.19592998 -0.08806116 0.0000000
Europe-Americas   0.37860342  0.33504186  0.42216498 0.0000000
Oceania-Americas  0.44550890  0.34106451  0.54995330 0.0000000
Europe-Asia       0.52059899  0.46851548  0.57268251 0.0000000
Oceania-Asia      0.58750447  0.47922837  0.69578058 0.0000000
Oceania-Europe    0.06690548 -0.03659526  0.17040622 0.3446553

Our initial hypothesis stands correct as the data reveals that Europe exhibits a higher suicide rate compared to other regions. Now, let’s explore if different regions within Europe exhibit distinct patterns in terms of suicide rates.

# Define a list of countries for each region
data_europe <- data %>%
  filter(continent == "Europe")

northern <- c("Denmark", "Estonia", "Finland", "Iceland", "Ireland", "Latvia", "Lithuania", "Norway", "Sweden", "United Kingdom","Luxembourg")
southern <- c("Greece", "Italy", "Portugal", "Spain","Malta")
eastern <- c("Bulgaria", "Czech Republic", "Hungary", "Poland", "Romania", "Russian Federation", "Slovakia","Albania","Ukraine","Belarus","Montenegro","Croatia", "Serbia", "Slovenia")
western <- c("Austria", "Belgium", "France", "Germany", "Netherlands", "Switzerland")


# Add a new column 'region' based on the country
data_europe$region <- case_when(
  data_europe$country %in% northern ~ "Northern",
  data_europe$country %in% southern ~ "Southern",
  data_europe$country %in% eastern ~ "Eastern",
  data_europe$country %in% western ~ "Western",
  TRUE ~ NA_character_ # for countries not listed above
)
# Group the data by region and calculate the average suicide rate per 100k and the number of unique countries
region_data <- data_europe %>%
  group_by(region) %>%
  summarise(avg_suicide_rate_per100k = sum(suicides_no, na.rm = TRUE) / sum(population) * 1e5,
            num_countries = n_distinct(country)) 

# Create the bar plot
ggplot(region_data, aes(x = reorder(region, -avg_suicide_rate_per100k), y = avg_suicide_rate_per100k, fill = region)) +
  geom_col() +
  scale_fill_brewer(palette = "Spectral") +
  labs(x = "Region", y = "Average suicide rate per 100k", 
       title = "Average Suicide Rates per 100k for Regions in Europe") +
  theme_minimal() +
  theme(legend.position = "none")

The presented plot indicates that the suicide ratio in Eastern Europe is notably higher compared to other regions. This finding aligns with the observations made in the previous sections regarding the countries within this region.

3.2.2.3 Multivariate Analyisis

Our goal is to understand the temporal trends in suicide rates for each country. Rather than creating visualizations for all 93 countries, we adopt a more streamlined approach by fitting a linear regression model to the data for each country. This allows us to identify patterns of increase or decrease in suicide rates over time.

Specifically, we’re interested in the ‘year’ coefficient in our linear models. This coefficient signifies the rate of change in suicide rates over time. To control for multiple comparisons, we only consider those countries with a corrected p-value less than 0.05.

To summarize, we are identifying countries where there’s a statistically significant linear trend in suicide rates over time. These trends are then rank-ordered based on their rate of change, providing a clear picture of where suicide rates are increasing or decreasing most rapidly.

# Create a summary data frame, grouping by country and year
country_year <- data %>%
  group_by(country, year) %>%
  summarize(suicides = sum(suicides_no), 
            population = sum(population), 
            suicide_per_100k = (suicides / population) * 100000, 
            gdp_per_capita = mean(GDP_per_capita),
            .groups = "drop")  # Prevents the warning about groups

# Fit a linear model for each country, tidy the output, and filter for significant trends
country_year_trends <- country_year %>%
  nest(data = -country) %>%  # Use explicit naming to prevent warning
  mutate(model = map(data, ~ lm(suicide_per_100k ~ year, data = .)),
         tidied = map(model, broom::tidy)) %>%
  unnest(cols = c(tidied))

# Adjust p-values and filter for significant results
country_year_sig_trends <- country_year_trends %>%
  filter(term == "year") %>%
  mutate(p.adjusted = p.adjust(p.value, method = "holm")) %>%
  filter(p.adjusted < .05) %>%
  arrange(estimate)

# Make country an ordered factor
country_year_sig_trends <- mutate(country_year_sig_trends, country = factor(country, ordered = TRUE, levels = country))
ggplot(country_year_sig_trends, aes(x=country, y=estimate, col = estimate)) + 
  geom_point(stat='identity', size = 2) +
  geom_hline(yintercept = 0, col = "grey", size = 1) +
  scale_color_gradient(low = "green", high = "red") +
  geom_segment(aes(y = 0, 
                   x = country, 
                   yend = estimate, 
                   xend = country), size = 1) +
  labs(title="Change per year (Suicides per 100k)", 
       x = "Country", y = "Change Per Year (Suicides per 100k)") +
  scale_y_continuous(breaks = seq(-2, 2, 0.2), limits = c(-1.5, 1.5)) +
  theme(legend.position = "none",
        axis.text.y = element_text(size= 5)) +
  coord_flip()

Approximately half of the countries (48 out of 96) exhibit a linear change in suicide rates as time progresses. Among these 48 countries, 32 of them (about two-thirds) show a decreasing trend. Overall, this trend presents a positive picture. However, it is worth noting that the suicide rates in Guyana and Korea are a cause for concern as they display concerning patterns.

### Lets look at those countries with the steepest increasing trends

top12_increasing <- tail(country_year_sig_trends$country, 12)

country_year %>%
  filter(country %in% top12_increasing) %>%
  ggplot(aes(x = year, y = suicide_per_100k, col = country)) + 
  geom_point() + 
  geom_smooth(method = "lm") + 
  facet_wrap(~ country) + 
  theme(legend.position = "none") + 
  labs(title="12 Steepest Increasing Trends", 
       subtitle="Of countries with significant trends (p < 0.05)", 
       x = "Year", 
       y = "Suicides per 100k")

The historical data for Guyana raises concerns due to a seemingly improbable jump in the suicide rate. While Guyana is known for having high suicide rates, the sudden increase observed appears questionable. It is possible that changes in how suicide cases were classified or reported could have influenced this significant surge in the dat

continent_time <- data_continent %>%
  group_by(year, continent) %>%
  summarize(suicide_per_100k = (sum(as.numeric(suicides_no)) / sum(as.numeric(population))) * 100000, .groups="drop")

continent_time$continent <- factor(continent_time$continent, ordered = T, levels = continent$continent)

continent_time_plot <- ggplot(continent_time, aes(x = year, y = suicide_per_100k, col = factor(continent))) + 
  facet_grid(continent ~ ., scales = "free_y") + 
  geom_line() + 
  geom_point() + 
  labs(title = "Trends Over Time, by Continent", 
       x = "Year", 
       y = "Suicides per 100k", 
       color = "Continent") + 
  theme(legend.position = "none", title = element_text(size = 10)) + 
  scale_x_continuous(breaks = seq(1985, 2015, 5), minor_breaks = F)
print(continent_time_plot)

  • Europe, having the highest suicide rate overall, has experienced a consistent decline of approximately 40% since 1995.
  • By 2015, Europe’s suicide rate had converged with that of Asia and Oceania, showing similar levels.
  • In contrast to the global downward trend, Oceania and the Americas demonstrate an upward trajectory in suicide rates. This distinct pattern is concerning and calls for a thorough investigation into the underlying factors contributing to this rise, as well as the implementation of effective interventions.

3.2.3 Tempurate

We have three variables: avg_temp, max_temp, and min_temp. These variables represent the average, maximum, and minimum temperatures, respectively, for each country in each year.

3.2.3.1 Univariate Analysis

library(gridExtra)
# Function to create a list of plots for each variable
create_plots <- function(variable_name, data) {
  p1 <- ggplot(data, aes_string(variable_name)) +
    geom_boxplot() 

  p2 <- ggplot(data, aes_string(variable_name)) +
    geom_histogram(bins = 30, fill = "steelblue", color = "white") 

  p3 <- ggplot(data, aes_string(variable_name)) +
    geom_density(fill = "steelblue")

  list(p1, p2, p3)
}

# Create plots for each temperature variable
avg_temp_plots <- create_plots("avg_temp", data)
Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
Please use tidy evaluation idioms with `aes()`. 
See also `vignette("ggplot2-in-packages")` for more information.
min_temp_plots <- create_plots("min_temp", data)
max_temp_plots <- create_plots("max_temp", data)

# Arrange the plots in a grid
grid.arrange(grobs = c(avg_temp_plots, min_temp_plots, max_temp_plots), ncol = 3)

3.2.3.2 Bivariate Analysis

Before calculating the correlation between temperature variables and suicide ratio, it is important to remove extreme outliers from the dataset.

library(gridExtra)
library(knitr)

# Function to process each temperature variable
correlation_plots <- function(temp_var, data) {
  
  # Calculate and print the correlation before removing outliers
  corr_before <- cor(data[[temp_var]], data$suicide_ratio, use = "complete.obs")
  
  # Calculate quartiles and IQR
  Q1 <- quantile(data[[temp_var]], 0.25, na.rm = TRUE)
  Q3 <- quantile(data[[temp_var]], 0.75, na.rm = TRUE)
  IQR <- Q3 - Q1
  
  # Identify outliers
  outliers <- data[[temp_var]] < (Q1 - 1.5 * IQR) | data[[temp_var]] > (Q3 + 1.5 * IQR)
  
  # Remove outliers
  data_no_outliers <- data[!outliers, ]
  
  # Calculate and print the correlation after removing outliers
  corr_after <- cor(data_no_outliers[[temp_var]], data_no_outliers$suicide_ratio, use = "complete.obs")
  
  # Create scatter plot with a regression line
  plot <- ggplot(data_no_outliers, aes_string(x = temp_var, y = "suicide_ratio")) +
    geom_point(color="skyblue", size=0.5) +
    geom_smooth(method = lm, color = "pink") +
    labs(x = paste("Temperature (", temp_var, ")", sep = ""), y = "Suicide Ratio", 
         title = temp_var) +
    theme_minimal()
  
  return(list(corr_before = corr_before, corr_after = corr_after, plot = plot))
}

# Call the function for each temperature variable
result_avg_temp <- correlation_plots("avg_temp", data)
result_min_temp <- correlation_plots("min_temp", data)
result_max_temp <- correlation_plots("max_temp", data)

# Combine the plots into a grid
grid.arrange(result_avg_temp$plot, result_min_temp$plot, result_max_temp$plot, nrow=1, ncol=3)

# Combine the correlation results into a data frame and display as a table
correlation_results <- data.frame(
  Temperature_Variable = c("avg_temp", "min_temp", "max_temp"),
  Correlation_Before = c(result_avg_temp$corr_before, result_min_temp$corr_before, result_max_temp$corr_before),
  Correlation_After = c(result_avg_temp$corr_after, result_min_temp$corr_after, result_max_temp$corr_after)
)

print(correlation_results)
cor(data$min_temp,data$sqrt_suicide_ratio)
[1] -0.353432
cor(data$min_temp,data$log_suicide_ratio)
[1] -0.1532334

It appears that the temperature variables exhibit a stronger correlation with the square root of the suicide ratio (sqrt_suicide_ratio). This observation is noteworthy and should be taken into account during further analysis.

Now, similar to the population variable, let’s bin the temperature variables. It is important to note that when using the Jenks method, we classify the temperatures for each year separately. This approach helps reduce the influence of temperature variations over the years and provides a more accurate assessment of the temperature categories.

temp_data <- data %>%
  group_by(year, country) %>%
  summarize(temp = mean(avg_temp), .groups = "drop")
fisher_jenks <- function(x) {
  bins <- classIntervals(x, n = 5, style = "fisher")$brks
  cut(x, breaks = bins, labels = c("Freezing","Very_Cold", "Cold", "Warm","Hot" ), include.lowest = TRUE)
}

# Add the new column to the data frame
temp_data <- temp_data %>%
  group_by(year) %>%
  mutate(avg_temp_bine_jenks = fisher_jenks(temp))

# View the new data
head(temp_data)
temp_data
table(temp_data$avg_temp_bine_jenks)

 Freezing Very_Cold      Cold      Warm       Hot 
       77       301       697       375       831 

Considering the limited number of countries falling under the “Freezing” temperature category, it would be appropriate to combine the “Very Cold” and “Freezing” categories into a single category. This consolidation ensures that the temperature classification remains meaningful and representative despite the scarcity of data points in the “Freezing” category

temp_data <- temp_data %>%
  mutate(avg_temp_bine_jenks = ifelse(avg_temp_bine_jenks == "Freezing", "Very_Cold",
                                      as.character(avg_temp_bine_jenks)))
table(temp_data$avg_temp_bine_jenks)

     Cold       Hot Very_Cold      Warm 
      697       831       378       375 
# lets join this dataset to original dataset 
data<- data %>%
  left_join(temp_data%>%
  select(country,year,avg_temp_bine_jenks),by = c("year", "country"))

Now, let’s compare the suicide ratio in each climate category.

data_grouped <- data %>%
  group_by(avg_temp_bine_jenks) %>%
  summarise(
  suicide_ratio = mean(suicide_ratio) )
  
ggplot(data_grouped, aes(x = avg_temp_bine_jenks, y = suicide_ratio, fill = avg_temp_bine_jenks)) +
  geom_bar(stat = "identity", color = "black") +
  labs(x = "Average temp Bin", y = "Suicide Ratio", title = "Suicide Ratio for each Climate Category") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5), legend.position = "none") +
  scale_fill_brewer(palette = "Set2")

From the plots, it is evident that countries with hot and warm climates exhibit significantly lower suicide rates. Moreover, the plot suggests that we can simplify the climate categories into just two groups: “Warm” and “Not Warm” since the suicide rates in these two categories are similar.

data <- data %>%
  mutate(avg_temp_bine_jenks = ifelse(avg_temp_bine_jenks == "Hot", "Warm",
                                      as.character(avg_temp_bine_jenks)))
data <- data %>%
  mutate(avg_temp_bine_jenks = ifelse(avg_temp_bine_jenks == "Very_Cold", "Cold",
                                      as.character(avg_temp_bine_jenks)))

We can apply the same procedure of analysis to the variables of maximum temperature and mean temperature as well.

# MIN TEMP
temp_data <- data %>%
  group_by(year, country) %>%
  summarize(temp = mean(min_temp), .groups = "drop")

fisher_jenks <- function(x) {
  bins <- classIntervals(x, n = 5, style = "fisher")$brks
  cut(x, breaks = bins, labels = c("Freezing","Very_Cold", "Cold", "Warm","Hot" ), include.lowest = TRUE)
}

# Add the new column to the data frame
temp_data <- temp_data %>%
  group_by(year) %>%
  mutate(min_temp_bine_jenks = fisher_jenks(temp))

# since we have very low country for very_cold we unify very cold and cold also 
temp_data <- temp_data %>%
  mutate(min_temp_bine_jenks = ifelse(min_temp_bine_jenks == "Freezing", "Very_Cold",
                                      as.character(min_temp_bine_jenks)))

# Join to the data
data<- data %>%
  left_join(temp_data%>%
  select(country,year,min_temp_bine_jenks),by = c("year", "country"))
data_grouped <- data %>%
  group_by(min_temp_bine_jenks) %>%
  summarise(
  suicide_ratio = mean(suicide_ratio) )

ggplot(data_grouped, aes(x = min_temp_bine_jenks, y = suicide_ratio, fill = min_temp_bine_jenks)) +
  geom_bar(stat = "identity", color = "black") +
  labs(x = "min temp Bin", y = "Suicide Ratio", title = "Suicide Ratio for each min temp Bin") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5), legend.position = "none") +
  scale_fill_brewer(palette = "Set2")

data <- data %>%
  mutate(min_temp_bine_jenks = ifelse(min_temp_bine_jenks == "Warm", "Hot", as.character(min_temp_bine_jenks)))
# Max Temp

temp_data <- data %>%
  group_by(year, country) %>%
  summarize(temp = mean(max_temp))
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.
fisher_jenks <- function(x) {
  bins <- classIntervals(x, n = 5, style = "fisher")$brks
  cut(x, breaks = bins, labels = c("Freezing","Very_Cold", "Cold", "Warm","Hot" ), include.lowest = TRUE)
}

# Add the new column to the data frame
temp_data <- temp_data %>%
  group_by(year) %>%
  mutate(max_temp_bine_jenks = fisher_jenks(temp))

temp_data <- temp_data %>%
  mutate(max_temp_bine_jenks = ifelse(max_temp_bine_jenks == "Freezing", "Very_Cold",
                                      as.character(max_temp_bine_jenks)))

data<- data %>%
  left_join(temp_data%>%
  select(country,year,max_temp_bine_jenks),by = c("year", "country"))
data_grouped <- data %>%
  group_by(max_temp_bine_jenks) %>%
  summarise(
  suicide_ratio = mean(suicide_ratio) )

ggplot(data_grouped, aes(x = max_temp_bine_jenks, y = suicide_ratio, fill = max_temp_bine_jenks)) +
  geom_bar(stat = "identity", color = "black") +
  labs(x = "max temp Bin", y = "Suicide Ratio", title = "Suicide Ratio for each max temp Bin") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5), legend.position = "none") +
  scale_fill_brewer(palette = "Set2")

NA
NA
data <- data %>%
  mutate(max_temp_bine_jenks = ifelse(max_temp_bine_jenks == "Hot", "Warm", as.character(max_temp_bine_jenks)))
data <- data %>%
  mutate(max_temp_bine_jenks = ifelse(max_temp_bine_jenks == "Very_Cold", "Cold", as.character(max_temp_bine_jenks)))

3.3 Social and Economical

3.3.1 GDP

3.3.1.1 Univariate Analysis

# Scale the GDP and log GDP variables
data$scaled_GDP_for_year <- (data$GDP_for_year - min(data$GDP_for_year))/(max(data$GDP_for_year)-min(data$GDP_for_year))
data$scaled_log_GDP_year <- (data$log_GDP_year - min(data$log_GDP_year)) / (max(data$log_GDP_year) - min(data$log_GDP_year))
library(gridExtra)
# Plotting original GDP
p1 <- ggplot(data, aes(GDP_for_year)) +
  geom_histogram(binwidth=1000000000, fill="skyblue", color="black") +
  labs(title = "Histogram of GDP_for_year",
       x = "GDP_for_year",
       y = "Count")

p2 <- ggplot(data, aes(x = "", y = GDP_for_year)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of GDP_for_year",
       x = "",
       y = "GDP_for_year")

# Plotting scaled GDP
p3 <- ggplot(data, aes(scaled_GDP_for_year)) +
  geom_histogram(binwidth=0.01, fill="skyblue", color="black") +
  labs(title = "Histogram of scaled_GDP_for_year",
       x = "scaled_GDP_for_year",
       y = "Count")

p4 <- ggplot(data, aes(x = "", y = scaled_GDP_for_year)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of scaled_GDP_for_year",
       x = "",
       y = "scaled_GDP_for_year")

# Plotting scaled log GDP
p5 <- ggplot(data, aes(scaled_log_GDP_year)) +
  geom_histogram(binwidth=0.01, fill="skyblue", color="black") +
  labs(title = "Histogram of scaled_log_GDP_year",
       x = "scaled_log_GDP_year",
       y = "Count")

p6 <- ggplot(data, aes(x = "", y = scaled_log_GDP_year)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of scaled_log_GDP_year",
       x = "",
       y = "scaled_log_GDP_year")

# Arrange the plots in a grid
grid.arrange(p1, p2, p3, p4, p5, p6, ncol = 2)

# Computing scaled and log-scaled GDP per capita
data$scaled_GDP_per_capita <- (data$GDP_per_capita - min(data$GDP_per_capita)) / 
                              (max(data$GDP_per_capita) - min(data$GDP_per_capita))

data$scaled_log_GDP_capita <- (data$log_GDP_capita-min(data$log_GDP_capita))/
                              (max(data$log_GDP_capita)-min(data$log_GDP_capita)) 
# Loading required library
library(gridExtra)

# Original GDP per Capita
p1 <- ggplot(data, aes(GDP_per_capita)) +
  geom_histogram(binwidth=1000, fill="skyblue", color="black") +
  labs(title = "Histogram of GDP_per_Capita",
       x = "GDP_per_Capita",
       y = "Count")

p2 <- ggplot(data, aes(x = "", y = GDP_per_capita)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of GDP_per_Capita",
       x = "",
       y = "GDP_per_Capita")

# Scaled GDP per Capita
p3 <- ggplot(data, aes(scaled_GDP_per_capita)) +
  geom_histogram(binwidth=0.01, fill="skyblue", color="black") +
  labs(title = "Histogram of Scaled GDP_per_Capita",
       x = "Scaled GDP_per_Capita",
       y = "Count")

p4 <- ggplot(data, aes(x = "", y = scaled_GDP_per_capita)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of Scaled GDP_per_Capita",
       x = "",
       y = "Scaled GDP_per_Capita")

# Scaled Log GDP per Capita
p5 <- ggplot(data, aes(scaled_log_GDP_capita)) +
  geom_histogram(binwidth=0.01, fill="skyblue", color="black") +
  labs(title = "Histogram of Scaled Log GDP_per_Capita",
       x = "Scaled Log GDP_per_Capita",
       y = "Count")

p6 <- ggplot(data, aes(x = "", y = scaled_log_GDP_capita)) +
  geom_boxplot(fill="lightgreen", color="black") +
  labs(title = "Box Plot of Scaled Log GDP_per_Capita",
       x = "",
       y = "Scaled Log GDP_per_Capita")

# Arrange the plots in a grid
grid.arrange(p1, p2, p3, p4, p5, p6, ncol = 2)

3.3.1.2 Bivariate Analysis

# Prepare the data in long format
long_data <- data %>% 
  select(scaled_log_GDP_year, suicide_ratio, log_suicide_ratio, sqrt_suicide_ratio) %>%
  gather(key = "Variable", value = "Value", -scaled_log_GDP_year)

# Create the plot
ggplot(long_data, aes(x = scaled_log_GDP_year, y = Value)) +
  geom_point(color = "skyblue", size=0.5) +
  facet_wrap(~Variable, scales = "free", ncol = 3) +
  ggtitle("Scatterplots of scaled_log_GDP_year vs. different variables") +
  theme_minimal() +
  ylab("") +
  xlab("scaled_log_GDP_year")

cor(data$scaled_log_GDP_year, data$suicide_ratio)
[1] 0.1049844
cor(data$scaled_log_GDP_year, data$log_suicide_ratio)
[1] -0.06932113
cor(data$scaled_log_GDP_year, data$sqrt_suicide_ratio)
[1] 0.2291764
# Prepare the data in long format
long_data <- data %>% 
  select(scaled_log_GDP_capita, suicide_ratio, log_suicide_ratio, sqrt_suicide_ratio) %>%
  gather(key = "Variable", value = "Value", -scaled_log_GDP_capita)

# Create the plot
ggplot(long_data, aes(x = scaled_log_GDP_capita, y = Value)) +
  geom_point(color = "skyblue", size=0.5) +
  facet_wrap(~Variable, scales = "free", ncol = 3) +
  ggtitle("Scatterplots of scaled_log_GDP_year vs. different variables") +
  theme_minimal() +
  ylab("") +
  xlab("scaled_log_GDP_year")

cor(data$scaled_log_GDP_capita, data$suicide_ratio)
[1] 0.01539712
cor(data$scaled_log_GDP_capita, data$log_suicide_no)
[1] 0.1347893
cor(data$scaled_log_GDP_capita, data$sqrt_suicide_ratio)
[1] 0.06002621
cor(data$GDP_per_capita, data$suicide_ratio)
[1] 0.002272913
cor(data$GDP_per_capita, data$log_suicide_no)
[1] 0.1111765
cor(data$GDP_per_capita, data$sqrt_suicide_ratio)
[1] 0.05862302

I am surprised to discover that there seems to be no evident influence between GDP and suicide rates. Let’s further investigate this matter.

Considering the fact that GDP tends to increase over the years for countries, it becomes clear that GDP alone may not be a reliable indicator of a country’s overall wealth or prosperity.

To gain deeper insights, we can introduce a new column called “gpd_pro_cap,” which represents the share of each individual within each cluster divided by the sum of the GDP values for all countries. This calculation provides an estimation of the share of each individual within each cluster relative to the total GDP of the world during those respective years.By incorporating this new measure, we aim to capture the average share of each individual within each cluster in each year, accounting for the global GDP. This approach allows us to evaluate the relative economic position of individuals within their respective clusters over time.

Let’s analyze the correlation between the average GDP per capita and the years.

# Compute average GDP_per_capita for each year
df_yearly <- data %>%
  group_by(year) %>%
  summarise(avg_GDP_per_capita = mean(GDP_per_capita, na.rm = TRUE))

correlation <- cor(df_yearly$year, df_yearly$avg_GDP_per_capita)
print(correlation)
[1] 0.9450579

To address this issue, we can categorize GDP per capita similar to how we categorized the population variable earlier.

gdp_data <- data %>%
  group_by(year, country) %>%
  summarize(income = mean(GDP_per_capita, na.rm = TRUE), .groups = "drop")
fisher_jenks <- function(x) {
  bins <- classIntervals(x, n = 4, style = "fisher")$brks
  cut(x, breaks = bins, labels = c("Very_Low_income","Low_income", "Medium_income", "High_income" ), include.lowest = TRUE)
}

# Add the new column to the data frame
gdp_data <- gdp_data %>%
  group_by(year) %>%
  mutate(gdp_per_capita_bine_jenks = fisher_jenks(income))

# View the new data
table(gdp_data$gdp_per_capita_bine_jenks)

Very_Low_income      Low_income   Medium_income     High_income 
           1259             420             457             145 
data<- data %>%
  left_join(gdp_data%>%
  select(country,year,gdp_per_capita_bine_jenks),by = c("year", "country"))
data_sum <- data %>%
  group_by(country, year) %>%
  summarise(GDP_per_capita = mean(GDP_per_capita), .groups = "drop")

thresholds <- quantile(data_sum$GDP_per_capita, probs = c(0.25, 0.5, 0.75))

# Assign each country-year pair to a population category
data_binned <- data_sum %>%
  mutate(
    gdp_bine_median = case_when(
      GDP_per_capita <= thresholds[1] ~ "Very_low_income",
      GDP_per_capita > thresholds[1] & GDP_per_capita <= thresholds[2] ~ "low_income",
      GDP_per_capita > thresholds[2] & GDP_per_capita <= thresholds[3] ~ "Medium_income",
      TRUE ~ "high_income"
    )
  )

data <- data %>%
  left_join(data_binned%>%
  select(country,year,gdp_bine_median),by = c("year", "country"))

By using the GDP binning method (gdp_bine_jenks) for each year, we mitigate the impact of the increasing GDP over time. Now, let’s examine the suicide ratio within each GDP category to gain further insights.

# Calculate the mean suicide_ratio for each group
mean_suicide_ratio <- data %>%
  group_by(gdp_per_capita_bine_jenks) %>%
  summarise(mean_suicide_ratio = mean(suicide_ratio))

# Print the mean_suicide_ratio dataframe
print(mean_suicide_ratio)
# Plot the mean_suicide_ratio
ggplot(mean_suicide_ratio, aes(x = gdp_per_capita_bine_jenks, y = mean_suicide_ratio, fill = gdp_per_capita_bine_jenks)) +
  geom_col(show.legend = FALSE) + # Remove the color legend
  scale_fill_brewer(palette = "Set2") + # Change the color palette
  labs(title = "Mean Suicide Ratio by GDP Group",
       x = "GDP Group (Jenks Natural Breaks)",
       y = "Mean Suicide Ratio") +
  theme_minimal() + # Use a clean theme
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) # Rotate x-axis labels for better readability

It appears that countries with lower GDP per capita tend to exhibit lower suicide rates. This observation suggests a possible inverse relationship between the economic status of a country and its suicide rate.

3.3.1.3 Multivariate Analysis

In this section, we aim to explore the relationship between a country’s economic prosperity and its suicide rate. Specifically, we investigate the question: “As a country gets richer, does its suicide rate decrease?”

It depends on the country - for almost every country, there is a high correlation between year and gdp per capita, i.e. as time goes on, gdp per capita linearly increases.

country_year_gdp <- data %>%
  group_by(country, year) %>%
  summarize(GDP_per_capita = mean(GDP_per_capita), .groups = "drop")
  
country_year_gdp_corr <- country_year_gdp %>%
  ungroup() %>%
  group_by(country) %>%
  summarize(year_gdp_correlation = cor(year, GDP_per_capita), .groups = "drop")

In our analysis, we examined the relationship between ‘year’ and ‘GDP per capita’ within individual countries by calculating the Pearson correlations. The results were intriguing: the mean correlation was 0.878, indicating a very strong positive linear relationship. Essentially, this suggests that an increase in wealth per person within a country is correlated with an increase in the country’s suicide rate over time.

However, it’s crucial to note that these trends are not uniform across all countries. While some countries show an increase in suicide rates over time, most are actually experiencing a decrease.

This leads us to ask a slightly different but equally significant question: Do wealthier countries have higher suicide rates? To explore this, we calculated the mean GDP per capita across all available years for each country, then compared this with the average suicide rate over the same period. This approach provides us with a single data point for each country, offering a general impression of a nation’s affluence and its suicide rate.

country_mean_gdp <- data %>%
  group_by(country, continent) %>%
  summarize(suicide_per_100k = (sum(as.numeric(suicides_no)) / sum(as.numeric(population))) * 100000, 
            gdp_per_capita = mean(GDP_per_capita), .groups = "drop")

ggplot(country_mean_gdp, aes(x = gdp_per_capita, y = suicide_per_100k, col = continent)) + 
  geom_point() + 
  scale_x_continuous(labels=scales::dollar_format(prefix="$"), breaks = seq(0, 70000, 10000)) + 
  labs(title = "Correlation between GDP (per capita) and Suicides per 100k", 
       subtitle = "Plot containing every country",
       x = "GDP (per capita)", 
       y = "Suicides per 100k", 
       col = "Continent") 

A number of countries in our dataset exhibit high leverage and residuals, potentially influencing the fit of our regression line. A notable example is Lithuania, which is situated in the top left of our graph. To mitigate this impact, we’ll apply Cook’s Distance as a measure to identify and exclude outliers. We will exclude those countries with a Cook’s Distance value greater than 4/n, which is a common threshold.

After implementing this adjustment, we’ll examine the revised model, now free of outliers, to better understand its statistical properties.

model1 <- lm(suicide_per_100k ~ gdp_per_capita, data = country_mean_gdp)

gdp_suicide_no_outliers <- model1 %>%
  augment() %>%
  arrange(desc(.cooksd)) %>%
  filter(.cooksd < 4/nrow(.)) %>% # removes 5/93 countries
  inner_join(country_mean_gdp, by = c("suicide_per_100k", "gdp_per_capita")) %>%
  select(country, continent, gdp_per_capita, suicide_per_100k)

model2 <- lm(suicide_per_100k ~ gdp_per_capita, data = gdp_suicide_no_outliers)

summary(model2)

Call:
lm(formula = suicide_per_100k ~ gdp_per_capita, data = gdp_suicide_no_outliers)

Residuals:
    Min      1Q  Median      3Q     Max 
-13.347  -6.413  -2.431   5.619  25.158 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    1.182e+01  1.378e+00   8.579 3.27e-13 ***
gdp_per_capita 8.095e-05  6.208e-05   1.304    0.196    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.102 on 87 degrees of freedom
Multiple R-squared:  0.01917,   Adjusted R-squared:  0.007895 
F-statistic:   1.7 on 1 and 87 DF,  p-value: 0.1957

Based on our analysis, we cannot reject the null hypothesis, which suggests that there is no linear association between the suicide rate per 100,000 population and GDP per capita for each country. However, we anticipate that when we incorporate these variables with other factors, it may reveal a linear association. We will further explore this relationship in the upcoming model chapter.

Check if the new features made any problem in the dataset.

unfactorized_vars <- function(df) {
  var_names <- names(df)
  unfactorized <- var_names[sapply(df, function(x) is.character(x) | is.integer(x))]
  return(unfactorized)
}

# Testing the function
unfactorized_vars(data)
[1] "year"                "avg_temp_bine_jenks" "min_temp_bine_jenks" "max_temp_bine_jenks"
[5] "gdp_bine_median"    

lets factorize them

data$avg_temp_bine_jenks <- factor(data$avg_temp_bine_jenks, 
                   ordered = T, 
                   levels = c("Cold","Warm"))

data$min_temp_bine_jenks <- factor(data$min_temp_bine_jenks, 
                   ordered = T, 
                   levels = c("Very_Cold","Cold","Hot"))

data$max_temp_bine_jenks <- factor(data$max_temp_bine_jenks, 
                   ordered = T, 
                   levels = c("Cold","Warm"))

data$gdp_per_capita_bine_jenks <- factor(data$gdp_per_capita_bine_jenks, 
                   ordered = T, 
                   levels = c("Very_Low_income",
                              "Low_income", 
                              "Medium_income", 
                              "High_income"))
data$gdp_bine_median <- factor(data$gdp_bine_median, 
                   ordered = T, 
                   levels = c("Very_low_income",
                              "low_income", 
                              "Medium_income", 
                              "high_income"))
null_percentage <- function(df) {
  # Calculates the percentage of null values in each column of a dataframe

  # Get the number of nulls in each column
  nulls <- sapply(df, function(x) sum(is.na(x)))

  # Calculate the percentage
  percentages <- nulls / nrow(df) * 100

  # Return the result as a data frame for easier viewing
  return(data.frame(Column = names(df), NullPercentage = percentages))
}

# Usage:
null_percentage(data)
NA

3.4 Demographic Variables

For this specific part, we have data on age, generation, and sex variables. It is important to emphasize that our dataset is well-distributed among each sex and age group. Each sex and age bound is represented by a single row in our dataset, ensuring comprehensive coverage across different demographic categories.

3.3.1 Univariate Anaylysis

Given that our data is well-distributed across different sexes and age groups, we can proceed to visualize a bar plot for the generation variable. This will provide a visual representation of how the data is distributed among different generations.

# Define common theme for all plots
common_theme <- theme_minimal() +
  theme(
    legend.position = "none",
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

# Bar plot for Generation
generation_bar_plot <- data %>%
  ggplot(aes(x = generation, fill = generation)) +
  geom_bar() +
  labs(title = "Bar Plot of Generation",
       x = "Generation",
       y = "Count") +
  scale_fill_brewer(palette = "Set2") +
  common_theme


# Arrange plots
grid.arrange(generation_bar_plot, ncol = 1)

3.3.2 Bivariate Analysis

# Define the function
create_suicide_rate_plot <- function(group_var) {
  data %>%
    group_by(!!sym(group_var)) %>%
    summarize(suicide_per_100k = (sum(suicides_no) / sum(population)) * 100000) %>%
    ggplot(aes_string(x = group_var, y = "suicide_per_100k", fill = group_var)) +
    geom_col() +
    labs(
      #title = group_var, 
      x = group_var,
      y = ""
    ) +
    theme_minimal() +
    theme(
      legend.position = "none",
      plot.title = element_text(hjust = 1),
      axis.text.x = element_text(angle = 0, hjust = 0.5, vjust = 1, size= 4),
      axis.line.x = element_line(inherit.blank = TRUE)
    ) +
    coord_cartesian(ylim = c(0, 30)) + 
    scale_fill_brewer(palette = "Set2")
}

theme_update(plot.title = element_blank(), axis.title.y = element_blank())

sex_plot <- create_suicide_rate_plot("sex")
age_plot <- create_suicide_rate_plot("age")
generation_plot <- create_suicide_rate_plot("generation")

# Arrange the plots
grid.arrange(
  top = textGrob("Global suicides per 100k", gp=gpar(fontsize=16, fontface="bold")),
  left = textGrob("Suicides per 100k", rot=90, gp=gpar(fontsize=16, fontface="bold")),
  arrangeGrob(sex_plot, age_plot, generation_plot, ncol=3)
)

Based on the plots, we observe that suicides are more prevalent among men and the age group of 75 years and older. Regarding the generation variable, it appears that suicides were more common in the G.I. Generation (also known as the World War II generation). However, it is important to note that our data is not evenly distributed among the different generations, with limited data available for the G.I. Generation and Millennials. This necessitates further investigation to draw more reliable conclusions.

To validate the insights from the plots and determine their statistical significance, we will employ statistical tests. These tests will help assess if the observed patterns are statistically significant or merely due to random variation.

3.3.2.1 T_test for Sex

To determine if the assumption of Homogeneity of Variance is satisfied, we can employ the Levene’s test. This statistical test allows us to assess if the variances are equal across the different groups under consideration. By conducting the Levene’s test, we can evaluate if the Homogeneity of Variance assumption holds true in our data.

leveneTest(log_suicide_ratio ~ sex, data = data)
Levene's Test for Homogeneity of Variance (center = median)
         Df F value    Pr(>F)    
group     1  29.423 5.877e-08 ***
      22808                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
t.test(log_suicide_ratio ~ sex, data = data, var.equal = FALSE)

    Welch Two Sample t-test

data:  log_suicide_ratio by sex
t = -91.948, df = 22781, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Female and group Male is not equal to 0
95 percent confidence interval:
 -1.211644 -1.161065
sample estimates:
mean in group Female   mean in group Male 
           -9.770343            -8.583988 

The test results indicate that there is a statistically significant difference between males and females. This finding suggests that the suicide rates significantly vary between the two genders.

3.3.2.2 ANOVA for Age

Since we have multiple age groups to compare, we can employ the ANOVA (Analysis of Variance) test. The hypothesis for the ANOVA test is as follows:

H0: The mean suicide ratio is equal for all age groups. H1: There is at least one age group with a different mean suicide ratio.

By conducting the ANOVA test, we can determine if there is a statistically significant difference in the mean suicide ratios among the various age groups.

# Fit the model
age_anova <- aov(log_suicide_ratio ~ age, data = data)

# Run the ANOVA
anova_result <- anova(age_anova)

# Print the result
print(anova_result)
Analysis of Variance Table

Response: log_suicide_ratio
             Df Sum Sq Mean Sq F value    Pr(>F)    
age           4   2609  652.26  549.52 < 2.2e-16 ***
Residuals 22805  27069    1.19                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Testing Normality of Residuals Assumption for ANOVA

# Create a data frame for residuals
residuals_df <- data.frame(residuals = residuals(age_anova))

# Create histogram of residuals
hist_plot <- ggplot(residuals_df, aes(x = residuals)) +
  geom_histogram(fill = 'steelblue', color = 'black', bins = 30) +
  theme_minimal() +
  labs(x = "Residuals", y = "Frequency",
       title = "Histogram of Residuals")

# Create Q-Q plot of residuals
qq_plot <- ggplot(residuals_df, aes(sample = residuals)) +
  geom_qq(color = 'steelblue') +
  geom_qq_line(color = 'red') +
  theme_minimal() +
  labs(title = "Normal Q-Q Plot",
       x = "Theoretical Quantiles",
       y = "Sample Quantiles")

# Arrange the plots side by side using the gridExtra package
library(gridExtra)
grid.arrange(hist_plot, qq_plot, ncol = 2)

Testing Homogeneity of Variances Assumption for ANOVA

leveneTest(log_suicide_ratio ~ age, data = data)
Levene's Test for Homogeneity of Variance (center = median)
         Df F value    Pr(>F)    
group     4  55.038 < 2.2e-16 ***
      22805                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
bartlett.test(log_suicide_ratio ~ age, data = data)

    Bartlett test of homogeneity of variances

data:  log_suicide_ratio by age
Bartlett's K-squared = 250.28, df = 4, p-value < 2.2e-16

Since the p-value from both tests is small, we reject the null hypothesis, indicating that the variances are not equal across different groups. In this scenario, using the ANOVA test may not provide accurate results. As an alternative, we can employ the Kruskal-Wallis test, which is a non-parametric test suitable for situations where the assumption of equal variances is violated.

kruskal.test(log_suicide_ratio ~ age, data = data)

    Kruskal-Wallis rank sum test

data:  log_suicide_ratio by age
Kruskal-Wallis chi-squared = 2043.1, df = 4, p-value < 2.2e-16

The result of the Kruskal-Wallis test aligns with that of the ANOVA. The obtained p-value is significantly small, indicating that there is a statistically significant difference in the means of the target variable across the levels of the categorical variable. However, the ANOVA alone does not provide information about which specific groups have different means.

To identify the specific groups with significant mean differences, we can employ Tukey’s Honest Significant Difference (HSD) test. This post-hoc test allows us to conduct pairwise comparisons and determine which groups exhibit statistically significant differences in their means. By performing further investigations using the Tukey’s HSD test, we can gain more insights into the specific group differences.

TukeyHSD(age_anova)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = log_suicide_ratio ~ age, data = data)

$age
                 diff        lwr       upr   p adj
25-34-15-24 0.2162285 0.15399869 0.2784584 0.0e+00
35-54-15-24 0.3288462 0.26661639 0.3910761 0.0e+00
55-74-15-24 0.4999026 0.43767272 0.5621324 0.0e+00
75+-15-24   1.0029526 0.94072278 1.0651825 0.0e+00
35-54-25-34 0.1126177 0.05038784 0.1748475 7.7e-06
55-74-25-34 0.2836740 0.22144417 0.3459039 0.0e+00
75+-25-34   0.7867241 0.72449423 0.8489539 0.0e+00
55-74-35-54 0.1710563 0.10882648 0.2332862 0.0e+00
75+-35-54   0.6741064 0.61187654 0.7363362 0.0e+00
75+-55-74   0.5030501 0.44082021 0.5652799 0.0e+00

3.3.2.3 ANOVA for Generation

# Fit the model
generation_anova <- aov(log_suicide_ratio ~ generation, data = data)

# Run the ANOVA
anova_result <- anova(generation_anova)

# Print the result
print(anova_result)
Analysis of Variance Table

Response: log_suicide_ratio
              Df  Sum Sq Mean Sq F value    Pr(>F)    
generation     4  2251.8  562.95   468.1 < 2.2e-16 ***
Residuals  22805 27425.9    1.20                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
leveneTest(log_suicide_ratio ~ generation, data = data)
Levene's Test for Homogeneity of Variance (center = median)
         Df F value    Pr(>F)    
group     4  35.672 < 2.2e-16 ***
      22805                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
kruskal.test(log_suicide_ratio ~ generation, data = data)

    Kruskal-Wallis rank sum test

data:  log_suicide_ratio by generation
Kruskal-Wallis chi-squared = 1807.2, df = 4, p-value < 2.2e-16

The obtained small p-values indicate that there is a statistically significant difference in the suicide rate among different generations. This finding suggests that the suicide rates vary significantly across the different generational cohorts.

3.3.3 Multivariate Analysis

3.3.3.2 Age differences, by Continent

global_average <- (sum(as.numeric(data$suicides_no)) / sum(as.numeric(data$population))) * 100000

data %>%
  group_by(continent, age) %>%
  summarize(n = n(), 
            suicides = sum(as.numeric(suicides_no)), 
            population = sum(as.numeric(population)), 
            suicide_per_100k = (suicides / population) * 100000, .groups= "drop") %>%
  ggplot(aes(x = continent, y = suicide_per_100k, fill = age)) + 
  geom_bar(stat = "identity", position = "dodge") + 
  geom_hline(yintercept = global_average, linetype = 2, color = "grey35", size = 1) +
  labs(title = "Age Disparity, by Continent",
       x = "Continent", 
       y = "Suicides per 100k", 
       fill = "Age")+
  coord_flip()

In the regions of the Americas, Asia, and Europe, which comprise the majority of the dataset, the suicide rate tends to increase with age. However, it is important to note that for Oceania and Africa, the highest suicide rates are observed among individuals aged 25 to 34. Nevertheless, due to the limited availability of data for Africa, this particular finding may not be entirely reliable. Further investigation and data collection are necessary to provide more accurate insights into the suicide rates in Africa.

3.3.3.3 Gender differences, by Continent

data %>%
  group_by(continent, sex) %>%
  summarize(n = n(), 
            suicides = sum(as.numeric(suicides_no)), 
            population = sum(as.numeric(population)), 
            suicide_per_100k = (suicides / population) * 100000,
            .groups = "drop") %>%
  ggplot(aes(x = continent, y = suicide_per_100k, fill = sex)) + 
  geom_bar(stat = "identity", position = "dodge") + 
  geom_hline(yintercept = global_average, linetype = 2, color = "grey35", size = 1) +
  labs(title = "Gender Disparity, by Continent",
   x = "Continent", 
   y = "Suicides per 100k", 
   fill = "Sex") +
  coord_flip()

Between 1985 and 2015, European men faced the highest risk of suicide, with a rate of approximately 30 suicides per 100,000 population per year. In comparison, Asia had the lowest overrepresentation of male suicide, with the suicide rate for men being around 2.5 times higher than that for women. Conversely, in Europe, the male suicide rate was approximately 3.9 times higher than the female suicide rate, indicating a greater disparity between genders in suicide rates compared to Asia.

3.3.3.4 Gender differences, by Country

# Overall suicide rate by country and continent
country_long <- data %>%
  group_by(country, continent) %>%
  summarize(suicide_per_100k = (sum(suicides_no, na.rm = TRUE) / sum(population, na.rm = TRUE)) * 1e5, .groups = "drop") %>%
  mutate(sex = "OVERALL")

# Suicide rate by country, continent, and sex
sex_country_long <- data %>%
  group_by(country, continent, sex) %>%
  summarize(suicide_per_100k = (sum(suicides_no, na.rm = TRUE) / sum(population, na.rm = TRUE)) * 1e5, .groups = "drop")

# Pivot the data to wide format for visualization, and calculate the difference between Male and Female suicide rates
sex_country_wide <- sex_country_long %>%
  pivot_wider(names_from = sex, values_from = suicide_per_100k) %>%
  arrange(Male - Female)

# Convert 'country' to ordered factor based on difference in suicide rates between Male and Female
ordered_countries <- sex_country_wide$country
sex_country_wide$country <- factor(sex_country_wide$country, ordered = TRUE, levels = ordered_countries)
sex_country_long$country <- factor(sex_country_long$country, ordered = TRUE, levels = ordered_countries)

# Visualization
ggplot(sex_country_wide, aes(y = country, color = sex)) + 
  geom_dumbbell(aes(x=Female, xend=Male), color = "grey", size = 0.5) + 
  geom_point(data = sex_country_long, aes(x = suicide_per_100k), size = 0.5) +
  geom_point(data = country_long, aes(x = suicide_per_100k), size = 0.5) + 
  geom_vline(xintercept = global_average, linetype = 2, color = "grey35", linewidth = 0.5) +
  theme(axis.text.y = element_text(size = 1), legend.position = c(0.85, 0.2)) + 
  scale_x_continuous(breaks = seq(0, round(max(sex_country_wide$Male, na.rm = TRUE) + 10, -1), 10)) +
  labs(title = "Gender Disparity, by Continent & Country", 
       subtitle = "Ordered by difference in deaths per 100k.", 
       x = "Suicides per 100k", 
       y = "Country", 
       color = "Sex")

country_gender_prop <- sex_country_wide %>%
  mutate(Male_Proportion = Male / (Female + Male)) %>%
  arrange(Male_Proportion)

sex_country_long$country <- factor(sex_country_long$country, 
                                   ordered = T,
                                   levels = country_gender_prop$country)

ggplot(sex_country_long, aes(y = suicide_per_100k, x = country, fill = sex)) + 
  geom_bar(position = "fill", stat = "identity") +
  scale_y_continuous(labels = scales::percent) +
  labs(title = "Proportions of suicides that are Male & Female, by Country", 
       x = "Country", 
       y = "Suicides per 100k",
       fill = "Sex") + 
  coord_flip() +
  theme(
  legend.position = "right",
  legend.key.size = unit(0.25, "cm"),
  plot.title = element_text(hjust = 0.5),
  axis.text.y = element_text(size= 4))

The over representation of men in suicide deaths is a widespread phenomenon observed in various countries. Although women may have higher rates of depression and suicidal thoughts, it is men who are more likely to die by suicide. This paradoxical pattern, known as the gender paradox in suicidal behavior, highlights the complex interplay of factors such as societal expectations, help-seeking behaviors, and coping mechanisms that contribute to the gender disparity in suicide rates. It underscores the need for further research and targeted interventions to address this issue and reduce the burden of suicide among both men and women. links

4.Model

4.1 modification feutures

In this chapter, we begin with a comprehensive overview of our variables, identifying their characteristics and potential areas for refinement. We subsequently make necessary adjustments to improve their suitability for our analysis

data1 <-data.frame(data)

In the initial phase of our analysis, we focus on refining our dataset for more accurate and meaningful results. Specifically, we remove certain columns that are not contributing to our understanding or prediction of the suicide ratio.

As we’ve discussed earlier, variables such as ‘population’, ‘suicide_no’, and their related transformations or scaled versions inherently have a strong association with our target, the ‘suicide_ratio’. The ‘suicide_ratio’ is an estimate of the likelihood of an individual committing suicide in a specific demographic group or country.

While it might seem that ‘population’ would be a beneficial predictor for the ‘suicide_ratio’, including it may skew our results, leading to biased estimations. This is because it’s not the mere size of the population, but specific characteristics within that population that lead to increased suicide ratios.

To navigate this challenge, we incorporate different features to transform and convey the crucial information contained in these variables, without directly using them. This way, we aim to create a model that captures the nuances and complexity of the factors contributing to the suicide ratio.

remove_var = c("sqrt_suicide_no","population","log_population","new_suicides_no","new_suicide_ratio","scaled_population","scaled_log_population","scaled_GDP_for_year","scaled_log_GDP_year","log_suicide_no","scaled_log_GDP_capita","sqrt_population","sqrt_suicide_no","suicides_no" )
data1 <- dplyr::select(data1, -dplyr::one_of(remove_var))

In the section dedicated to outlier analysis, we observed that the log-transformed version of ‘suicide_ratio’ is significantly more resilient to outliers than the original ‘suicide_ratio’ variable. This discovery makes ‘log(suicide_ratio)’ a preferred candidate for our target variable, especially given its normal distribution which is a desirable property for many statistical models.

To prepare our dataset for further modeling, we standardize our predictors by rescaling them to have a mean of zero and a standard deviation of one.Finally, to maintain a tidy dataset, we drop the original untransformed and unscaled columns. This leaves us with a clean, standardized dataset that is ready for the next stages of our analysis and modeling process.

scale_columns <- function(data, columns_to_scale) {
  # Loop over the columns
  for (col in columns_to_scale) {
    # Check if the column exists and is not all NA
    if (!col %in% names(data) || all(is.na(data[[col]]))) {
      message(paste("Column", col, "does not exist or is all NA. Skipping..."))
      next
    }
    # Create a new column name
    new_col_name <- paste0("scaled_", col)
    
    # Scale the column
    data[[new_col_name]] <- scale(data[[col]])
  }
  
  # Drop the original columns
  data <- data[, !(names(data) %in% columns_to_scale)]
  
  return(data)
}
scaled_var = c("GDP_for_year","GDP_per_capita","life_exp","avg_temp","min_temp","max_temp","log_GDP_year","log_GDP_capita","sqrt_GDP_year","sqrt_GDP_capita","log_suicide_ratio","suicide_ratio","sqrt_suicide_ratio")
data2 <- scale_columns(data1,scaled_var)

4.2 Multicollinearity

4.2.1 Continuous Variable

In this section, we employ techniques such as heatmaps and Variance Inflation Factor (VIF) to investigate potential collinearity among our variables.

A heatmap is a valuable visualization tool that illustrates the correlation matrix through a gradient color scheme. By visually representing the correlation coefficients, a heatmap can reveal patterns and relationships among variables, highlighting any potential multicollinearity issues.

On the other hand, VIF is a numerical measure that quantifies the severity of multicollinearity in a regression analysis. It gauges the amount of multicollinearity by examining how much the variance of the estimated regression coefficients is increased due to multicollinearity. A high VIF suggests a high degree of collinearity with other variables, warranting attention.

These techniques collectively give us a holistic view of the correlation structure among our variables, aiding in feature selection and model performance improvement.

# we create a new dataframe which only includes numeric columns using sapply
numeric_data <- data2[sapply(data2, is.numeric)]


width <- 25
height <- 25
options(repr.plot.width = width, repr.plot.height = height)


corr_matrix <- cor(numeric_data)

# Round the correlation matrix to 3 decimal places
rounded_corr <- round(corr_matrix, 3)

# Create the correlation plot
ggcorrplot(rounded_corr, 
           lab = TRUE, 
           lab_size = 1.5, 
           method = "circle", 
           pch = 1, 
           colors = c("red", "#ebebeb", "#13527a")) +
  theme(axis.text.x = element_text(size = 10))

there is strong corrilation between some of the variables

VIF
remove_var <- c("scaled_sqrt_suicide_ratio","scaled_log_suicide_ratio")
data_simple <-dplyr::select(numeric_data, -dplyr::one_of(remove_var))
remove_var <- c("scaled_sqrt_suicide_ratio","scaled_suicide_ratio")
data_log <-dplyr::select(numeric_data, -dplyr::one_of(remove_var))
remove_var <- c("scaled_log_suicide_ratio","scaled_suicide_ratio")
data_sqrt <-dplyr::select(numeric_data, -dplyr::one_of(remove_var))

and for each target we make a vif graph

mod.linear <- lm(scaled_suicide_ratio~ ., data = data_simple)
vifs <- data.frame(vif(mod.linear))

ggplot(vifs, aes(y=vif.mod.linear., x=row.names(vifs))) + 
    geom_bar(aes(fill=vif.mod.linear.>5),stat="identity")+
    scale_y_continuous(trans = "sqrt",  breaks = c(5, 10, 50, 100))+
    geom_hline(yintercept = 5, colour = "red") + 
    ggtitle("VIF per feature for suicide_ratio as target") +
    xlab("Featurs") + ylab("VIF") +
    theme(axis.text.x=element_text(angle=20, hjust=1))+
    theme(text = element_text(size = 10))+
    scale_fill_brewer(palette="RdYlBu")

As observable from our analysis, numerous variables exhibit high VIF, a sign of multicollinearity. This isn’t surprising given that many variables were derived from one another through transformations. To handle this, we need to employ a strategy of variable clustering. Each cluster would contain variables that are highly correlated with one another, indicating potential multicollinearity.

From each cluster, we would then select the variable that strikes the best balance between having the highest correlation with our target and the least susceptibility to outliers. This approach enables us to maintain essential information while mitigating the negative effects of multicollinearity, thereby enhancing our model’s predictive performance.

gdp_var <-c("scaled_GDP_for_year","scaled_GDP_per_capita","scaled_log_GDP_year", "scaled_log_GDP_capita","scaled_sqrt_GDP_year","scaled_sqrt_GDP_capita")
temp_var<-c("scaled_min_temp","scaled_avg_temp","scaled_max_temp")

for suicide_ratio scaled_min_temp and scaled_GPD_per_year lets do this test again

mod.linear <- lm(scaled_suicide_ratio~ ., data = subset((data_simple),select = c(year,scaled_life_exp,scaled_min_temp,scaled_GDP_for_year,scaled_suicide_ratio)))
vifs <- data.frame(vif(mod.linear))
ggplot(vifs, aes(y=vif.mod.linear., x=row.names(vifs))) + 
    geom_bar(aes(fill=vif.mod.linear.>5),stat="identity")+
    scale_y_continuous(trans = "sqrt",  breaks = c(5, 10, 50, 100))+
    geom_hline(yintercept = 5, colour = "red") + 
    ggtitle("VIF per feature for suicide_ratio as target") +
    xlab("Featurs") + ylab("VIF") +
    theme(axis.text.x=element_text(angle=20, hjust=1))+
    theme(text = element_text(size = 10))+
    scale_fill_brewer(palette="RdYlBu")

we do same for log_suicide_ratio and sqrt_suicide_ratio for log_suicide_ratio scaled_avg_temp and scaled_log_GDp_capita suicide_ratio scaled_min_temp and scaled_GPD_per_year lets do this test again

mod.linear <- lm(scaled_log_suicide_ratio~ ., data = subset((data_log),select = c(year,scaled_life_exp,scaled_avg_temp,scaled_log_GDP_capita,scaled_log_suicide_ratio)))
vifs <- data.frame(vif(mod.linear))

ggplot(vifs, aes(y=vif.mod.linear., x=row.names(vifs))) + 
    geom_bar(aes(fill=vif.mod.linear.>5),stat="identity")+
    scale_y_continuous(trans = "sqrt",  breaks = c(5, 10, 50, 100))+
    geom_hline(yintercept = 5, colour = "red") + 
    ggtitle("VIF per feature for suicide_ratio as target") +
    xlab("Featurs") + ylab("VIF") +
    theme(axis.text.x=element_text(angle=20, hjust=1))+
    theme(text = element_text(size = 10))+
    scale_fill_brewer(palette="RdYlBu")

suicide_ratio scaled_min_temp and scaled_GPD_per_year for log_suicide_ratio scaled_avg_temp and scaled_log_GDp_capita for sqrt_suicide_ratio scaled_avg_temp and scaled_log_GDp_capita lets do this test again

mod.linear <- lm(scaled_sqrt_suicide_ratio~ ., data = subset((data_sqrt),select = c(year,scaled_life_exp,scaled_log_GDP_year,scaled_avg_temp,scaled_sqrt_suicide_ratio)))
vifs <- data.frame(vif(mod.linear))

ggplot(vifs, aes(y=vif.mod.linear., x=row.names(vifs))) + 
    geom_bar(aes(fill=vif.mod.linear.>5),stat="identity")+
    scale_y_continuous(trans = "sqrt",  breaks = c(5, 10, 50, 100))+
    geom_hline(yintercept = 5, colour = "red") + 
    ggtitle("VIF per feature for suicide_ratio as target") +
    xlab("Featurs") + ylab("VIF") +
    theme(axis.text.x=element_text(angle=20, hjust=1))+
    theme(text = element_text(size = 10))+
    scale_fill_brewer(palette="RdYlBu")

as we can see all of them have value less than 5 and we can say that there is no coliniarity between these variable. ### 4.2.2 Categorical variable The concept of multicollinearity is a bit less straightforward when applied to categorical variables, particularly because categorical variables can take on limited, and usually few, distinct values.

However, multicollinearity can still occur with categorical variables. For example, suppose you have a dataset of cars, and you have two variables: “Brand” and “Country”. If every “Brand” uniquely maps to a “Country” (e.g., if ‘Toyota’ is always ‘Japan’, ‘Ford’ is always ‘USA’, etc.), then these two variables are perfectly multicollinear. we can use chi squre but chisq is very sensetive to unbalanced variable. we will ues Cramér’s V for categorical variables. Cramér’s V is a statistical measure that assesses the strength of association between two nominal variables. It is based on Pearson’s chi-squared statistic and was published by Harald Cramér in 1946.

Cramér’s V ranges from 0 (indicating no association between the variables) to 1 (indicating a perfect association). It could be seen as an extension of the correlation coefficient to nominal data.

Cramér’s V is symmetrical — it does not matter which variable we consider as independent or dependent. The formula for Cramér’s V is:

V = sqrt((X^2/n) / (min(k-1, r-1)))

where:

X^2 is the chi-squared statistic, n is the total sample size, k is the number of columns, r is the number of rows in the contingency table. Just like with correlation, a value close to 0 indicates little association between the variables, and a value close to 1 indicates a strong association. However, unlike correlation, Cramér’s V can only reach 1 in the case of complete association (all cells other than the diagonal are 0), or when the number of rows equals the number of columns. first seperate categorical var

factor_vars <- sapply(data, is.factor)

factor_vars_names <- names(data)[factor_vars]
factor_vars_names
 [1] "country"                   "sex"                       "age"                      
 [4] "generation"                "continent"                 "population_bine_jenks"    
 [7] "population_bine_median"    "avg_temp_bine_jenks"       "min_temp_bine_jenks"      
[10] "max_temp_bine_jenks"       "gdp_per_capita_bine_jenks" "gdp_bine_median"          

then we apply Cramér’s V for each pair of this variable.

# Retrieve all the categorical variable names
factor_vars_names <- names(data[sapply(data, is.factor)])

# Initialize a data frame to hold the Cramer's V values
V_df <- data.frame(matrix(nrow = length(factor_vars_names), ncol = length(factor_vars_names)))
names(V_df) <- factor_vars_names
rownames(V_df) <- factor_vars_names

# Loop over each pair of variables
for(i in 1:length(factor_vars_names)){
  for(j in 1:length(factor_vars_names)){
    if(i != j){
      
      # Create a contingency table
      tab <- table(data[[factor_vars_names[i]]], data[[factor_vars_names[j]]])
      
      # Perform Chi-square test
      chi_sq <- chisq.test(tab)
      
      # Calculate Cramer's V
      n <- sum(tab) # total number of observations
      k <- min(dim(tab)) # number of rows or columns (whichever is smaller)
      V <- sqrt(chi_sq$statistic / (n * (k - 1)))
      
      V_df[i,j] <- V
      
      cat("Cramer's V for", factor_vars_names[i], "and", factor_vars_names[j], ":", V, "\n")
      
    } else {
      V_df[i,j] <- NA
    }
  }
}
Cramer's V for country and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for country and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for country and generation : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for country and continent : 1 
Warning: Chi-squared approximation may be incorrect
Cramer's V for country and population_bine_jenks : 0.9555141 
Cramer's V for country and population_bine_median : 0.9658267 
Cramer's V for country and avg_temp_bine_jenks : 0.919768 
Cramer's V for country and min_temp_bine_jenks : 0.8440007 
Cramer's V for country and max_temp_bine_jenks : 0.9293818 
Warning: Chi-squared approximation may be incorrect
Cramer's V for country and gdp_per_capita_bine_jenks : 0.8451701 
Cramer's V for country and gdp_bine_median : 0.6755723 
Cramer's V for sex and country : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for sex and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for sex and generation : NaN 
Cramer's V for sex and continent : 0 
Cramer's V for sex and population_bine_jenks : 0 
Cramer's V for sex and population_bine_median : 0 
Cramer's V for sex and avg_temp_bine_jenks : 0 
Cramer's V for sex and min_temp_bine_jenks : 0 
Cramer's V for sex and max_temp_bine_jenks : 0 
Cramer's V for sex and gdp_per_capita_bine_jenks : 0 
Cramer's V for sex and gdp_bine_median : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and country : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and sex : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and generation : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and continent : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and population_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and population_bine_median : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and avg_temp_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and min_temp_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and max_temp_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and gdp_per_capita_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for age and gdp_bine_median : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and country : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and sex : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and continent : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and population_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and population_bine_median : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and avg_temp_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and min_temp_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and max_temp_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and gdp_per_capita_bine_jenks : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for generation and gdp_bine_median : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for continent and country : 1 
Cramer's V for continent and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for continent and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for continent and generation : NaN 
Cramer's V for continent and population_bine_jenks : 0.1360322 
Cramer's V for continent and population_bine_median : 0.2201354 
Cramer's V for continent and avg_temp_bine_jenks : 0.6364753 
Cramer's V for continent and min_temp_bine_jenks : 0.501039 
Cramer's V for continent and max_temp_bine_jenks : 0.626857 
Cramer's V for continent and gdp_per_capita_bine_jenks : 0.2519669 
Cramer's V for continent and gdp_bine_median : 0.2615279 
Warning: Chi-squared approximation may be incorrect
Cramer's V for population_bine_jenks and country : 0.9555141 
Cramer's V for population_bine_jenks and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for population_bine_jenks and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for population_bine_jenks and generation : NaN 
Cramer's V for population_bine_jenks and continent : 0.1360322 
Cramer's V for population_bine_jenks and population_bine_median : 0.5621135 
Cramer's V for population_bine_jenks and avg_temp_bine_jenks : 0.08665067 
Cramer's V for population_bine_jenks and min_temp_bine_jenks : 0.1588227 
Cramer's V for population_bine_jenks and max_temp_bine_jenks : 0.1572724 
Cramer's V for population_bine_jenks and gdp_per_capita_bine_jenks : 0.1833429 
Cramer's V for population_bine_jenks and gdp_bine_median : 0.09180943 
Cramer's V for population_bine_median and country : 0.9658267 
Cramer's V for population_bine_median and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for population_bine_median and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for population_bine_median and generation : NaN 
Cramer's V for population_bine_median and continent : 0.2201354 
Cramer's V for population_bine_median and population_bine_jenks : 0.5621135 
Cramer's V for population_bine_median and avg_temp_bine_jenks : 0.3237402 
Cramer's V for population_bine_median and min_temp_bine_jenks : 0.2851608 
Cramer's V for population_bine_median and max_temp_bine_jenks : 0.3904609 
Cramer's V for population_bine_median and gdp_per_capita_bine_jenks : 0.1419864 
Cramer's V for population_bine_median and gdp_bine_median : 0.1003279 
Cramer's V for avg_temp_bine_jenks and country : 0.919768 
Cramer's V for avg_temp_bine_jenks and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for avg_temp_bine_jenks and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for avg_temp_bine_jenks and generation : NaN 
Cramer's V for avg_temp_bine_jenks and continent : 0.6364753 
Cramer's V for avg_temp_bine_jenks and population_bine_jenks : 0.08665067 
Cramer's V for avg_temp_bine_jenks and population_bine_median : 0.3237402 
Cramer's V for avg_temp_bine_jenks and min_temp_bine_jenks : 0.844372 
Cramer's V for avg_temp_bine_jenks and max_temp_bine_jenks : 0.7520485 
Cramer's V for avg_temp_bine_jenks and gdp_per_capita_bine_jenks : 0.3638312 
Cramer's V for avg_temp_bine_jenks and gdp_bine_median : 0.260163 
Cramer's V for min_temp_bine_jenks and country : 0.8440007 
Cramer's V for min_temp_bine_jenks and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for min_temp_bine_jenks and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for min_temp_bine_jenks and generation : NaN 
Cramer's V for min_temp_bine_jenks and continent : 0.501039 
Cramer's V for min_temp_bine_jenks and population_bine_jenks : 0.1588227 
Cramer's V for min_temp_bine_jenks and population_bine_median : 0.2851608 
Cramer's V for min_temp_bine_jenks and avg_temp_bine_jenks : 0.844372 
Cramer's V for min_temp_bine_jenks and max_temp_bine_jenks : 0.7471725 
Cramer's V for min_temp_bine_jenks and gdp_per_capita_bine_jenks : 0.2685126 
Cramer's V for min_temp_bine_jenks and gdp_bine_median : 0.2143205 
Cramer's V for max_temp_bine_jenks and country : 0.9293818 
Cramer's V for max_temp_bine_jenks and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for max_temp_bine_jenks and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for max_temp_bine_jenks and generation : NaN 
Cramer's V for max_temp_bine_jenks and continent : 0.626857 
Cramer's V for max_temp_bine_jenks and population_bine_jenks : 0.1572724 
Cramer's V for max_temp_bine_jenks and population_bine_median : 0.3904609 
Cramer's V for max_temp_bine_jenks and avg_temp_bine_jenks : 0.7520485 
Cramer's V for max_temp_bine_jenks and min_temp_bine_jenks : 0.7471725 
Cramer's V for max_temp_bine_jenks and gdp_per_capita_bine_jenks : 0.3330357 
Cramer's V for max_temp_bine_jenks and gdp_bine_median : 0.249758 
Warning: Chi-squared approximation may be incorrect
Cramer's V for gdp_per_capita_bine_jenks and country : 0.8451701 
Cramer's V for gdp_per_capita_bine_jenks and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for gdp_per_capita_bine_jenks and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for gdp_per_capita_bine_jenks and generation : NaN 
Cramer's V for gdp_per_capita_bine_jenks and continent : 0.2519669 
Cramer's V for gdp_per_capita_bine_jenks and population_bine_jenks : 0.1833429 
Cramer's V for gdp_per_capita_bine_jenks and population_bine_median : 0.1419864 
Cramer's V for gdp_per_capita_bine_jenks and avg_temp_bine_jenks : 0.3638312 
Cramer's V for gdp_per_capita_bine_jenks and min_temp_bine_jenks : 0.2685126 
Cramer's V for gdp_per_capita_bine_jenks and max_temp_bine_jenks : 0.3330357 
Cramer's V for gdp_per_capita_bine_jenks and gdp_bine_median : 0.5334269 
Cramer's V for gdp_bine_median and country : 0.6755723 
Cramer's V for gdp_bine_median and sex : 0 
Warning: Chi-squared approximation may be incorrect
Cramer's V for gdp_bine_median and age : NaN 
Warning: Chi-squared approximation may be incorrect
Cramer's V for gdp_bine_median and generation : NaN 
Cramer's V for gdp_bine_median and continent : 0.2615279 
Cramer's V for gdp_bine_median and population_bine_jenks : 0.09180943 
Cramer's V for gdp_bine_median and population_bine_median : 0.1003279 
Cramer's V for gdp_bine_median and avg_temp_bine_jenks : 0.260163 
Cramer's V for gdp_bine_median and min_temp_bine_jenks : 0.2143205 
Cramer's V for gdp_bine_median and max_temp_bine_jenks : 0.249758 
Cramer's V for gdp_bine_median and gdp_per_capita_bine_jenks : 0.5334269 
# Replace NA values with 0
V_df[is.na(V_df)] <- 0


print(V_df)
NA
#install.packages("pheatmap")
library(pheatmap)

# Make the heatmap
pheatmap(V_df, color = colorRampPalette(c("navy", "white", "firebrick3"))(25))

As observed, the ‘country’ variable demonstrates significant associations with numerous variables. This is expected given that these variables were created via a ‘group_by’ operation on ‘country’.

However, the crucial observation is the substantial association among ‘avg_temp_bine_jenks’, ‘min_temp_bine_jenks’, and ‘max_temp_bine_jenks’. For model efficiency, we should select one from this set.

To guide this selection, we ran several linear models to evaluate compatibility between these temperature variables and our potential targets (‘suicide_ratio’, ‘log_suicide_ratio’, and ‘sqrt_suicide_ratio’).

We compiled a dataframe featuring our three targets and the three temperature variables. The dataframe entries represent the adjusted R-squared values for each corresponding pair, providing a basis for optimal feature selection.

targets <- c("scaled_suicide_ratio", "scaled_log_suicide_ratio", "scaled_sqrt_suicide_ratio")
variables <- c("avg_temp_bine_jenks","min_temp_bine_jenks", "max_temp_bine_jenks"
)

adjusted_r2 <- matrix(nrow = length(targets), ncol = length(variables))
rownames(adjusted_r2) <- targets
colnames(adjusted_r2) <- variables

# loop over each target and variable
for (target in targets) {
  for (var in variables) {
    
    formula <- as.formula(paste(target, var, sep = " ~ "))
    
    # fit the linear model
    model <- lm(formula, data = data2)
    
    adjusted_r2[target, var] <- summary(model)$adj.r.squared
  }
}

# convert the matrix to a data frame
adjusted_r2_df <- as.data.frame(adjusted_r2)


print(adjusted_r2_df)

Considering the three potential target variables, ‘min_temp_bine_jenks’ consistently shows better performance in terms of R-squared values.

Thus far, we have categorized our features into continuous and categorical candidates.

Our final feature candidates, as determined by their collinearity and correlation with the target variable, are as follows:

scaled_suicide_ratio_var <-c("year","country","sex","age","continent","population_bine_jenks","scaled_GDP_for_year","min_temp_bine_jenks","gdp_per_capita_bine_jenks","scaled_min_temp")
scaled_log_suicide_ratio_var <-c("year","country","sex","age","continent","population_bine_jenks","scaled_log_GDP_capita","min_temp_bine_jenks","gdp_per_capita_bine_jenks","scaled_avg_temp")
scaled_sqrt_suicide_ratio_var <-c("year","country","sex","age","continent","population_bine_jenks","scaled_log_GDP_capita" ,"min_temp_bine_jenks","gdp_per_capita_bine_jenks","scaled_avg_temp")

4.3 Models selection

so for we have 3 target variable and for each one we found different proper variable. in this section we inspect different models with different criteria

To apply linear regression we need to make sure that four conditions are satisfied:

1.No multicollinearity: no high correlation between the independent variables; 2.Linearity: there must be a linear relationship between the target variablesand the other variables; 3.Normality: the residuals must be normally distributed; 4.Homoscedasticity: the residuals must have a constant variance

in previoues section we inspect multiliniarity problem and gave proper solution for each targts lets first make a simple model for each variable and see which conditions will meet.

#suicide_ratio
formula <- as.formula(paste("scaled_suicide_ratio", "~", paste(scaled_suicide_ratio_var, collapse = " + ")))

model_suicide_ratio <- lm(formula, data = data2)
summary(model_suicide_ratio)

Call:
lm(formula = formula, data = data2)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.3028 -0.3612 -0.0627  0.2668  9.7397 

Coefficients: (4 not defined because of singularities)
                                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)                         14.8096072  1.1780043  12.572  < 2e-16 ***
year                                -0.0078654  0.0005886 -13.364  < 2e-16 ***
countryAntigua and Barbuda          -0.1950023  0.1017517  -1.916 0.055320 .  
countryArgentina                     0.4781366  0.0878931   5.440 5.38e-08 ***
countryArmenia                       0.0076525  0.0668824   0.114 0.908908    
countryAruba                         0.3885659  0.1158588   3.354 0.000798 ***
countryAustralia                     0.6009979  0.0854668   7.032 2.09e-12 ***
countryAustria                       1.2725577  0.0763190  16.674  < 2e-16 ***
countryAzerbaijan                   -0.1115302  0.0702925  -1.587 0.112604    
countryBahamas                      -0.1172060  0.0987623  -1.187 0.235339    
countryBahrain                      -0.0838931  0.0875715  -0.958 0.338076    
countryBarbados                     -0.0541948  0.1045530  -0.518 0.604221    
countryBelarus                       1.6607261  0.0720502  23.050  < 2e-16 ***
countryBelgium                       1.1143428  0.0726324  15.342  < 2e-16 ***
countryBelize                        0.1584722  0.0937904   1.690 0.091110 .  
countryBrazil                       -0.3606057  0.1659609  -2.173 0.029803 *  
countryBulgaria                      0.9547131  0.0610557  15.637  < 2e-16 ***
countryCanada                        0.6390917  0.1274398   5.015 5.35e-07 ***
countryChile                         0.4254113  0.0595136   7.148 9.06e-13 ***
countryColombia                      0.1572541  0.1158979   1.357 0.174848    
countryCosta Rica                    0.1950856  0.0997969   1.955 0.050616 .  
countryCroatia                       1.2095605  0.0651363  18.570  < 2e-16 ***
countryCuba                          1.0789805  0.0958202  11.260  < 2e-16 ***
countryCyprus                        0.0414392  0.0876464   0.473 0.636361    
countryCzech Republic                0.9262792  0.0655212  14.137  < 2e-16 ***
countryDenmark                       0.7333559  0.1338261   5.480 4.30e-08 ***
countryEcuador                       0.1478544  0.0900761   1.641 0.100720    
countryEl Salvador                   0.4216083  0.0988820   4.264 2.02e-05 ***
countryEstonia                       1.4605530  0.0737846  19.795  < 2e-16 ***
countryFiji                          0.1297922  0.1080724   1.201 0.229774    
countryFinland                       1.2114869  0.0876298  13.825  < 2e-16 ***
countryFrance                        1.1373617  0.1085814  10.475  < 2e-16 ***
countryGeorgia                       0.0704287  0.0658792   1.069 0.285055    
countryGermany                       0.7967927  0.1106660   7.200 6.21e-13 ***
countryGreece                        0.0369190  0.0669733   0.551 0.581468    
countryGrenada                      -0.0819338  0.1039626  -0.788 0.430642    
countryGuatemala                    -0.0266986  0.0894393  -0.299 0.765316    
countryGuyana                        1.0963582  0.1028369  10.661  < 2e-16 ***
countryHungary                       1.8039871  0.0642018  28.099  < 2e-16 ***
countryIceland                       0.5972588  0.0766395   7.793 6.82e-15 ***
countryIreland                       0.4620733  0.0691062   6.686 2.34e-11 ***
countryIsrael                        0.3335001  0.0766149   4.353 1.35e-05 ***
countryItaly                         0.3379881  0.1045898   3.232 0.001233 ** 
countryJamaica                      -0.2071754  0.1040103  -1.992 0.046397 *  
countryJapan                         0.6649714  0.1535815   4.330 1.50e-05 ***
countryKazakhstan                    1.6279453  0.0811298  20.066  < 2e-16 ***
countryKiribati                      0.1185982  0.1149850   1.031 0.302353    
countryKuwait                       -0.1142358  0.0816881  -1.398 0.161995    
countryKyrgyzstan                    0.6596865  0.0774432   8.518  < 2e-16 ***
countryLatvia                        1.5848700  0.0729427  21.728  < 2e-16 ***
countryLithuania                     2.2618971  0.0719226  31.449  < 2e-16 ***
countryLuxembourg                    0.8192498  0.0780965  10.490  < 2e-16 ***
countryMalta                         0.0861410  0.0763500   1.128 0.259231    
countryMauritius                     0.4770723  0.0895077   5.330 9.92e-08 ***
countryMexico                        0.0641878  0.1103882   0.581 0.560927    
countryMontenegro                    0.4242783  0.0821029   5.168 2.39e-07 ***
countryNetherlands                   0.4754732  0.0725494   6.554 5.73e-11 ***
countryNew Zealand                   0.6503765  0.0679812   9.567  < 2e-16 ***
countryNicaragua                     0.2601074  0.1260364   2.064 0.039053 *  
countryNorway                        0.6018882  0.0861976   6.983 2.98e-12 ***
countryPanama                        0.1366746  0.1031305   1.325 0.185098    
countryParaguay                      0.0334652  0.0832579   0.402 0.687727    
countryPhilippines                  -0.0580201  0.1333516  -0.435 0.663500    
countryPoland                        0.7925555  0.0908728   8.722  < 2e-16 ***
countryPortugal                      0.4613540  0.0700223   6.589 4.53e-11 ***
countryPuerto Rico                   0.3867521  0.1009459   3.831 0.000128 ***
countryQatar                        -0.0152730  0.1007138  -0.152 0.879466    
countryRepublic of Korea             1.3593647  0.0909233  14.951  < 2e-16 ***
countryRomania                       0.5954025  0.0752994   7.907 2.75e-15 ***
countryRussian Federation            1.4538614  0.1749608   8.310  < 2e-16 ***
countrySaint Lucia                   0.2035607  0.1022536   1.991 0.046521 *  
countrySaint Vincent and Grenadines  0.1338394  0.1037865   1.290 0.197215    
countrySerbia                        1.1779278  0.0694849  16.952  < 2e-16 ***
countrySeychelles                    0.2565989  0.1077194   2.382 0.017222 *  
countrySingapore                     0.8289333  0.1086373   7.630 2.43e-14 ***
countrySlovakia                      0.5691105  0.0675672   8.423  < 2e-16 ***
countrySlovenia                      1.4988496  0.0727224  20.611  < 2e-16 ***
countrySouth Africa                 -0.0492529  0.0975861  -0.505 0.613766    
countrySpain                         0.4190532  0.0917002   4.570 4.91e-06 ***
countrySri Lanka                     1.8635695  0.1132795  16.451  < 2e-16 ***
countrySuriname                      1.0361597  0.1022244  10.136  < 2e-16 ***
countrySweden                        0.7217350  0.0824977   8.749  < 2e-16 ***
countrySwitzerland                   1.0080982  0.0833744  12.091  < 2e-16 ***
countryThailand                      0.2167633  0.1212717   1.787 0.073883 .  
countryTrinidad and Tobago           0.5834628  0.1017326   5.735 9.86e-09 ***
countryTurkey                        0.0504615  0.1176621   0.429 0.668023    
countryTurkmenistan                  0.3174057  0.0602745   5.266 1.41e-07 ***
countryUkraine                       1.4231449  0.0940799  15.127  < 2e-16 ***
countryUnited Arab Emirates         -0.0519189  0.1190825  -0.436 0.662847    
countryUnited Kingdom                0.2943397  0.1068800   2.754 0.005893 ** 
countryUnited States                 0.1547038  0.1814318   0.853 0.393844    
countryUruguay                       0.9545288  0.0710045  13.443  < 2e-16 ***
countryUzbekistan                    0.2992853  0.0684480   4.372 1.23e-05 ***
sexMale                              0.8977982  0.0088677 101.243  < 2e-16 ***
age.L                                0.5429783  0.0099144  54.767  < 2e-16 ***
age.Q                                0.1022368  0.0099144  10.312  < 2e-16 ***
age.C                                0.1129714  0.0099144  11.395  < 2e-16 ***
age^4                                0.0557506  0.0099144   5.623 1.90e-08 ***
continentAmericas                           NA         NA      NA       NA    
continentAsia                               NA         NA      NA       NA    
continentEurope                             NA         NA      NA       NA    
continentOceania                            NA         NA      NA       NA    
population_bine_jenks.L              0.3398234  0.0975265   3.484 0.000494 ***
population_bine_jenks.Q              0.2752480  0.0636123   4.327 1.52e-05 ***
population_bine_jenks.C              0.0686367  0.0362801   1.892 0.058523 .  
scaled_GDP_for_year                  0.0023482  0.0126586   0.185 0.852839    
min_temp_bine_jenks.L               -0.0072806  0.0256081  -0.284 0.776177    
min_temp_bine_jenks.Q                0.0080247  0.0143677   0.559 0.576494    
gdp_per_capita_bine_jenks.L         -0.0311668  0.0373266  -0.835 0.403741    
gdp_per_capita_bine_jenks.Q          0.0046895  0.0215878   0.217 0.828030    
gdp_per_capita_bine_jenks.C          0.0246067  0.0163459   1.505 0.132242    
scaled_min_temp                      0.0110464  0.0480953   0.230 0.818345    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6696 on 22702 degrees of freedom
Multiple R-squared:  0.5537,    Adjusted R-squared:  0.5516 
F-statistic: 263.2 on 107 and 22702 DF,  p-value: < 2.2e-16
data2 <- data2%>%
  filter(age != '5-14')
#log_suicide_ratio
formula <- as.formula(paste("scaled_log_suicide_ratio", "~", paste(scaled_log_suicide_ratio_var, collapse = " + ")))

model_log_suicide_ratio <- lm(formula, data = data2)
summary(model_log_suicide_ratio)

Call:
lm(formula = formula, data = data2)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.9213 -0.2769  0.0356  0.2978  2.6194 

Coefficients: (4 not defined because of singularities)
                                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)                          6.7180196  1.6954611   3.962 7.44e-05 ***
year                                -0.0042127  0.0008389  -5.022 5.16e-07 ***
countryAntigua and Barbuda           2.3031385  0.1042100  22.101  < 2e-16 ***
countryArgentina                     1.0919542  0.0683369  15.979  < 2e-16 ***
countryArmenia                      -0.2704081  0.0514003  -5.261 1.45e-07 ***
countryAruba                         2.4977499  0.1176818  21.225  < 2e-16 ***
countryAustralia                     1.5900352  0.0841628  18.892  < 2e-16 ***
countryAustria                       1.3962189  0.0737829  18.923  < 2e-16 ***
countryAzerbaijan                   -0.6475496  0.0517029 -12.524  < 2e-16 ***
countryBahamas                       1.2983243  0.1005920  12.907  < 2e-16 ***
countryBahrain                       1.1454880  0.1071545  10.690  < 2e-16 ***
countryBarbados                      1.3589670  0.1053554  12.899  < 2e-16 ***
countryBelarus                       1.5162001  0.0621441  24.398  < 2e-16 ***
countryBelgium                       1.5168695  0.0644161  23.548  < 2e-16 ***
countryBelize                        1.8965059  0.0941706  20.139  < 2e-16 ***
countryBrazil                        0.8763168  0.1407621   6.226 4.88e-10 ***
countryBulgaria                      1.2235238  0.0465456  26.287  < 2e-16 ***
countryCanada                        0.6104271  0.1359913   4.489 7.20e-06 ***
countryChile                         0.6945639  0.0521095  13.329  < 2e-16 ***
countryColombia                      0.8520593  0.1033498   8.244  < 2e-16 ***
countryCosta Rica                    1.1210072  0.0975155  11.496  < 2e-16 ***
countryCroatia                       1.5605500  0.0518298  30.109  < 2e-16 ***
countryCuba                          1.9695686  0.0964438  20.422  < 2e-16 ***
countryCyprus                        0.6170689  0.0773764   7.975 1.60e-15 ***
countryCzech Republic                1.1850130  0.0589826  20.091  < 2e-16 ***
countryDenmark                       0.0497837  0.2054182   0.242 0.808509    
countryEcuador                       0.8988255  0.0759110  11.841  < 2e-16 ***
countryEl Salvador                   1.4183500  0.0938476  15.113  < 2e-16 ***
countryEstonia                       1.4605259  0.0699135  20.890  < 2e-16 ***
countryFiji                          1.2484271  0.0993617  12.564  < 2e-16 ***
countryFinland                       1.2762077  0.0942812  13.536  < 2e-16 ***
countryFrance                        1.6880783  0.0840841  20.076  < 2e-16 ***
countryGeorgia                      -0.1408546  0.0531395  -2.651 0.008039 ** 
countryGermany                       1.2442436  0.0880506  14.131  < 2e-16 ***
countryGreece                        0.1883795  0.0560814   3.359 0.000783 ***
countryGrenada                       2.1928018  0.1047615  20.931  < 2e-16 ***
countryGuatemala                     0.3155934  0.0830149   3.802 0.000144 ***
countryGuyana                        2.2232707  0.0998517  22.266  < 2e-16 ***
countryHungary                       1.7311197  0.0523611  33.061  < 2e-16 ***
countryIceland                       1.1061485  0.0960449  11.517  < 2e-16 ***
countryIreland                       0.9067602  0.0634780  14.285  < 2e-16 ***
countryIsrael                        1.1020853  0.0745122  14.791  < 2e-16 ***
countryItaly                         0.8383662  0.0813901  10.301  < 2e-16 ***
countryJamaica                      -0.5097171  0.1027016  -4.963 6.99e-07 ***
countryJapan                         1.6595416  0.1117632  14.849  < 2e-16 ***
countryKazakhstan                    1.5948075  0.0629122  25.350  < 2e-16 ***
countryKiribati                      2.6790982  0.1098995  24.378  < 2e-16 ***
countryKuwait                        0.6872664  0.1027728   6.687 2.33e-11 ***
countryKyrgyzstan                    0.7558994  0.0729091  10.368  < 2e-16 ***
countryLatvia                        1.5259161  0.0669703  22.785  < 2e-16 ***
countryLithuania                     1.8628210  0.0648461  28.727  < 2e-16 ***
countryLuxembourg                    1.4596858  0.0708076  20.615  < 2e-16 ***
countryMalta                         1.0610733  0.0676287  15.690  < 2e-16 ***
countryMauritius                     1.6682225  0.0857462  19.455  < 2e-16 ***
countryMexico                        0.5250957  0.0941143   5.579 2.44e-08 ***
countryMontenegro                    0.4509376  0.0617213   7.306 2.84e-13 ***
countryNetherlands                   0.9999114  0.0649489  15.395  < 2e-16 ***
countryNew Zealand                   1.2168806  0.0595049  20.450  < 2e-16 ***
countryNicaragua                     1.0953209  0.1128264   9.708  < 2e-16 ***
countryNorway                        0.8551319  0.1035942   8.255  < 2e-16 ***
countryPanama                        1.0266985  0.1019069  10.075  < 2e-16 ***
countryParaguay                      0.6673450  0.0839018   7.954 1.89e-15 ***
countryPhilippines                   0.3131289  0.1220407   2.566 0.010301 *  
countryPoland                        1.1537004  0.0742494  15.538  < 2e-16 ***
countryPortugal                      0.9194589  0.0562908  16.334  < 2e-16 ***
countryPuerto Rico                   1.2015571  0.1005941  11.945  < 2e-16 ***
countryQatar                         1.2947877  0.1177229  10.999  < 2e-16 ***
countryRepublic of Korea             1.7351080  0.0712638  24.348  < 2e-16 ***
countryRomania                       0.9682255  0.0586017  16.522  < 2e-16 ***
countryRussian Federation            1.1776785  0.1499683   7.853 4.25e-15 ***
countrySaint Lucia                   2.0867597  0.1041426  20.038  < 2e-16 ***
countrySaint Vincent and Grenadines  2.2824205  0.1049011  21.758  < 2e-16 ***
countrySerbia                        1.3787293  0.0522034  26.411  < 2e-16 ***
countrySeychelles                    2.5356266  0.1066803  23.768  < 2e-16 ***
countrySingapore                     1.9514624  0.1083592  18.009  < 2e-16 ***
countrySlovakia                      0.5910512  0.0599467   9.860  < 2e-16 ***
countrySlovenia                      1.6481195  0.0600362  27.452  < 2e-16 ***
countrySouth Africa                 -0.7609252  0.0762778  -9.976  < 2e-16 ***
countrySpain                         0.9693693  0.0740129  13.097  < 2e-16 ***
countrySri Lanka                     2.4984897  0.1114334  22.421  < 2e-16 ***
countrySuriname                      2.3072065  0.1008992  22.866  < 2e-16 ***
countrySweden                        1.0246462  0.0901457  11.367  < 2e-16 ***
countrySwitzerland                   1.3875146  0.0768983  18.043  < 2e-16 ***
countryThailand                      1.3273935  0.1162242  11.421  < 2e-16 ***
countryTrinidad and Tobago           1.8540224  0.1020284  18.172  < 2e-16 ***
countryTurkey                       -0.0427117  0.0879368  -0.486 0.627178    
countryTurkmenistan                  0.8744881  0.0478318  18.283  < 2e-16 ***
countryUkraine                       1.5377334  0.0711577  21.610  < 2e-16 ***
countryUnited Arab Emirates          0.6835672  0.1299405   5.261 1.45e-07 ***
countryUnited Kingdom                0.7018319  0.0872288   8.046 8.98e-16 ***
countryUnited States                 1.0228650  0.1171657   8.730  < 2e-16 ***
countryUruguay                       1.5931893  0.0587398  27.123  < 2e-16 ***
countryUzbekistan                    0.7599959  0.0494784  15.360  < 2e-16 ***
sexMale                              1.0400454  0.0064787 160.534  < 2e-16 ***
age.L                                0.6347366  0.0072434  87.630  < 2e-16 ***
age.Q                                0.1480973  0.0072434  20.446  < 2e-16 ***
age.C                                0.1207620  0.0072434  16.672  < 2e-16 ***
age^4                                0.0116838  0.0072434   1.613 0.106750    
continentAmericas                           NA         NA      NA       NA    
continentAsia                               NA         NA      NA       NA    
continentEurope                             NA         NA      NA       NA    
continentOceania                            NA         NA      NA       NA    
population_bine_jenks.L              0.0392961  0.0713593   0.551 0.581858    
population_bine_jenks.Q              0.1488624  0.0465793   3.196 0.001396 ** 
population_bine_jenks.C             -0.0054825  0.0266270  -0.206 0.836872    
scaled_log_GDP_capita               -0.0811281  0.0160452  -5.056 4.31e-07 ***
min_temp_bine_jenks.L                0.0255438  0.0181665   1.406 0.159711    
min_temp_bine_jenks.Q                0.0006281  0.0104879   0.060 0.952246    
gdp_per_capita_bine_jenks.L          0.0413718  0.0283975   1.457 0.145163    
gdp_per_capita_bine_jenks.Q         -0.0455872  0.0156630  -2.910 0.003612 ** 
gdp_per_capita_bine_jenks.C          0.0079187  0.0119083   0.665 0.506077    
scaled_avg_temp                     -0.3907802  0.0604192  -6.468 1.01e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4892 on 22702 degrees of freedom
Multiple R-squared:  0.7618,    Adjusted R-squared:  0.7606 
F-statistic: 678.4 on 107 and 22702 DF,  p-value: < 2.2e-16
#ssqrt_uicide_ratio
formula <- as.formula(paste("scaled_sqrt_suicide_ratio", "~", paste(scaled_sqrt_suicide_ratio_var, collapse = " + ")))

model_sqrt_suicide_ratio <- lm(formula, data = data2)
summary(model_sqrt_suicide_ratio)

Call:
lm(formula = formula, data = data2)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.8872 -0.3105 -0.0238  0.2646  5.0725 

Coefficients: (4 not defined because of singularities)
                                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)                          1.7377932  1.9504611   0.891 0.372957    
year                                -0.0015243  0.0009651  -1.579 0.114251    
countryAntigua and Barbuda          -0.0470087  0.1198834  -0.392 0.694973    
countryArgentina                     0.9197022  0.0786149  11.699  < 2e-16 ***
countryArmenia                      -0.1586816  0.0591310  -2.684 0.007290 ** 
countryAruba                         0.8199850  0.1353814   6.057 1.41e-09 ***
countryAustralia                     1.4014686  0.0968210  14.475  < 2e-16 ***
countryAustria                       1.3067676  0.0848799  15.395  < 2e-16 ***
countryAzerbaijan                   -0.2800007  0.0594791  -4.708 2.52e-06 ***
countryBahamas                       0.1353162  0.1157212   1.169 0.242283    
countryBahrain                       0.3317641  0.1232707   2.691 0.007122 ** 
countryBarbados                      0.3269507  0.1212010   2.698 0.006989 ** 
countryBelarus                       1.4867219  0.0714907  20.796  < 2e-16 ***
countryBelgium                       1.3624497  0.0741044  18.386  < 2e-16 ***
countryBelize                        0.5619979  0.1083340   5.188 2.15e-07 ***
countryBrazil                        0.6147527  0.1619330   3.796 0.000147 ***
countryBulgaria                      1.0956692  0.0535461  20.462  < 2e-16 ***
countryCanada                        0.3861691  0.1564447   2.468 0.013579 *  
countryChile                         0.5849833  0.0599468   9.758  < 2e-16 ***
countryColombia                      0.8933616  0.1188937   7.514 5.95e-14 ***
countryCosta Rica                    0.9640306  0.1121820   8.593  < 2e-16 ***
countryCroatia                       1.4197400  0.0596251  23.811  < 2e-16 ***
countryCuba                          1.8406770  0.1109491  16.590  < 2e-16 ***
countryCyprus                        0.2902955  0.0890140   3.261 0.001111 ** 
countryCzech Republic                1.0380430  0.0678536  15.298  < 2e-16 ***
countryDenmark                      -0.1453713  0.2363134  -0.615 0.538453    
countryEcuador                       0.7787321  0.0873281   8.917  < 2e-16 ***
countryEl Salvador                   1.2168885  0.1079624  11.271  < 2e-16 ***
countryEstonia                       1.3377213  0.0804286  16.632  < 2e-16 ***
countryFiji                          0.6648421  0.1143058   5.816 6.10e-09 ***
countryFinland                       1.1244944  0.1084613  10.368  < 2e-16 ***
countryFrance                        1.5136200  0.0967305  15.648  < 2e-16 ***
countryGeorgia                      -0.0452623  0.0611317  -0.740 0.459062    
countryGermany                       1.0291344  0.1012936  10.160  < 2e-16 ***
countryGreece                        0.3030441  0.0645162   4.697 2.65e-06 ***
countryGrenada                       0.1267199  0.1205178   1.051 0.293058    
countryGuatemala                     0.4477438  0.0955004   4.688 2.77e-06 ***
countryGuyana                        1.7893650  0.1148696  15.577  < 2e-16 ***
countryHungary                       1.7441202  0.0602363  28.955  < 2e-16 ***
countryIceland                       0.4077317  0.1104902   3.690 0.000225 ***
countryIreland                       0.7051437  0.0730252   9.656  < 2e-16 ***
countryIsrael                        0.9734890  0.0857189  11.357  < 2e-16 ***
countryItaly                         0.7046088  0.0936313   7.525 5.45e-14 ***
countryJamaica                      -0.0101301  0.1181480  -0.086 0.931673    
countryJapan                         1.2482093  0.1285726   9.708  < 2e-16 ***
countryKazakhstan                    1.5140052  0.0723743  20.919  < 2e-16 ***
countryKiribati                      0.3689476  0.1264285   2.918 0.003524 ** 
countryKuwait                        0.3049219  0.1182300   2.579 0.009913 ** 
countryKyrgyzstan                    0.5131143  0.0838747   6.118 9.65e-10 ***
countryLatvia                        1.4503434  0.0770427  18.825  < 2e-16 ***
countryLithuania                     1.9300974  0.0745991  25.873  < 2e-16 ***
countryLuxembourg                    1.0266482  0.0814572  12.604  < 2e-16 ***
countryMalta                         0.3542329  0.0778002   4.553 5.31e-06 ***
countryMauritius                     1.2355644  0.0986426  12.526  < 2e-16 ***
countryMexico                        0.6190524  0.1082692   5.718 1.09e-08 ***
countryMontenegro                    0.0628834  0.0710042   0.886 0.375827    
countryNetherlands                   0.7821369  0.0747173  10.468  < 2e-16 ***
countryNew Zealand                   0.9719696  0.0684545  14.199  < 2e-16 ***
countryNicaragua                     0.9345654  0.1297956   7.200 6.20e-13 ***
countryNorway                        0.5667789  0.1191750   4.756 1.99e-06 ***
countryPanama                        0.9083036  0.1172339   7.748 9.74e-15 ***
countryParaguay                      0.5843253  0.0965207   6.054 1.44e-09 ***
countryPhilippines                   0.5446347  0.1403958   3.879 0.000105 ***
countryPoland                        0.9754330  0.0854166  11.420  < 2e-16 ***
countryPortugal                      0.8361846  0.0647570  12.913  < 2e-16 ***
countryPuerto Rico                   1.1769231  0.1157236  10.170  < 2e-16 ***
countryQatar                         0.4628545  0.1354286   3.418 0.000633 ***
countryRepublic of Korea             1.5946813  0.0819820  19.452  < 2e-16 ***
countryRomania                       0.7877192  0.0674154  11.685  < 2e-16 ***
countryRussian Federation            0.9644425  0.1725238   5.590 2.29e-08 ***
countrySaint Lucia                   0.7036431  0.1198058   5.873 4.33e-09 ***
countrySaint Vincent and Grenadines  0.4564925  0.1206784   3.783 0.000156 ***
countrySerbia                        1.2614840  0.0600549  21.006  < 2e-16 ***
countrySeychelles                    0.6373098  0.1227251   5.193 2.09e-07 ***
countrySingapore                     1.7707911  0.1246566  14.205  < 2e-16 ***
countrySlovakia                      0.6093188  0.0689628   8.835  < 2e-16 ***
countrySlovenia                      1.5529088  0.0690658  22.484  < 2e-16 ***
countrySouth Africa                  0.0358944  0.0877501   0.409 0.682504    
countrySpain                         0.8377337  0.0851445   9.839  < 2e-16 ***
countrySri Lanka                     2.4989936  0.1281932  19.494  < 2e-16 ***
countrySuriname                      1.8225122  0.1160746  15.701  < 2e-16 ***
countrySweden                        0.7800047  0.1037037   7.521 5.62e-14 ***
countrySwitzerland                   1.1669437  0.0884640  13.191  < 2e-16 ***
countryThailand                      1.1080801  0.1337045   8.288  < 2e-16 ***
countryTrinidad and Tobago           1.5051819  0.1173736  12.824  < 2e-16 ***
countryTurkey                        0.1436625  0.1011627   1.420 0.155588    
countryTurkmenistan                  0.6481341  0.0550257  11.779  < 2e-16 ***
countryUkraine                       1.4168806  0.0818600  17.309  < 2e-16 ***
countryUnited Arab Emirates          0.5240639  0.1494837   3.506 0.000456 ***
countryUnited Kingdom                0.5120429  0.1003482   5.103 3.38e-07 ***
countryUnited States                 0.6277121  0.1347876   4.657 3.23e-06 ***
countryUruguay                       1.4272601  0.0675744  21.121  < 2e-16 ***
countryUzbekistan                    0.5540797  0.0569200   9.734  < 2e-16 ***
sexMale                              0.9933181  0.0074531 133.276  < 2e-16 ***
age.L                                0.4277256  0.0083328  51.330  < 2e-16 ***
age.Q                               -0.0084772  0.0083328  -1.017 0.309009    
age.C                                0.0660510  0.0083328   7.927 2.35e-15 ***
age^4                                0.0441416  0.0083328   5.297 1.19e-07 ***
continentAmericas                           NA         NA      NA       NA    
continentAsia                               NA         NA      NA       NA    
continentEurope                             NA         NA      NA       NA    
continentOceania                            NA         NA      NA       NA    
population_bine_jenks.L              0.2040991  0.0820918   2.486 0.012918 *  
population_bine_jenks.Q              0.2142950  0.0535849   3.999 6.38e-05 ***
population_bine_jenks.C              0.0181399  0.0306318   0.592 0.553728    
scaled_log_GDP_capita               -0.0797017  0.0184584  -4.318 1.58e-05 ***
min_temp_bine_jenks.L                0.0181663  0.0208988   0.869 0.384718    
min_temp_bine_jenks.Q               -0.0036666  0.0120653  -0.304 0.761208    
gdp_per_capita_bine_jenks.L          0.0367725  0.0326685   1.126 0.260336    
gdp_per_capita_bine_jenks.Q         -0.0152593  0.0180188  -0.847 0.397086    
gdp_per_capita_bine_jenks.C          0.0287815  0.0136993   2.101 0.035657 *  
scaled_avg_temp                     -0.3927445  0.0695063  -5.650 1.62e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5628 on 22702 degrees of freedom
Multiple R-squared:  0.6847,    Adjusted R-squared:  0.6832 
F-statistic: 460.8 on 107 and 22702 DF,  p-value: < 2.2e-16

Before we delve deeper into our linear regression analysis, it’s crucial to emphasize that the assumptions underpinning this model don’t need to be flawlessly met. However, severe violations can skew the model’s accuracy and lead to misleading results.

Now, let’s turn our attention to evaluating other necessary conditions for our regression model.

plot(model_suicide_ratio,1)

plot(model_log_suicide_ratio,1)

plot(model_sqrt_suicide_ratio,1)

The linearity condition doesn’t appear to be perfectly satisfied for any of our targets. However, the residuals for the log-transformed target are reasonably well-distributed and do not demonstrate any discernible patterns. On the other hand, the suicide_ratio and sqrt_suicide_ratio targets, particularly the former, do display unusual patterns in the residuals graph. Let’s continue our evaluation by assessing the next assumption of our model.

plot(model_suicide_ratio,2)

plot(model_log_suicide_ratio,2)

plot(model_sqrt_suicide_ratio,2)

library(olsrr)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘olsrr’

The following object is masked from ‘package:datasets’:

    rivers
ols_plot_resid_hist(model_suicide_ratio)

ols_plot_resid_hist(model_log_suicide_ratio)

ols_plot_resid_hist(model_sqrt_suicide_ratio)

As observed, the majority of our residuals for the log_suicide_ratio and sqrt_suicide_ratio models lie within -1 and 1, and -1.5 and 1.5 respectively, and their distributions largely follow a normal pattern. However, the suicide_ratio model doesn’t appear to satisfy these conditions as effectively.

To evaluate the final assumption - homoscedasticity of residuals - we apply the Breusch-Pagan test. The test’s null hypothesis assumes homoscedasticity. If the p-value is significant (generally, less than 0.05), it suggests a deviation from this assumption, indicating heteroscedasticity.

library(lmtest)
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric
bptest(model_suicide_ratio)

    studentized Breusch-Pagan test

data:  model_suicide_ratio
BP = 2875.6, df = 107, p-value < 2.2e-16
bptest(model_log_suicide_ratio)

    studentized Breusch-Pagan test

data:  model_log_suicide_ratio
BP = 3430.8, df = 107, p-value < 2.2e-16
bptest(model_sqrt_suicide_ratio)

    studentized Breusch-Pagan test

data:  model_sqrt_suicide_ratio
BP = 4770, df = 107, p-value < 2.2e-16
plot(model_log_suicide_ratio,3)

plot(model_sqrt_suicide_ratio,3)

The plot visualizes the residuals’ variance in relation to the predictors. Ideally, the residuals should be randomly scattered around the centerline, signifying homoscedasticity.

In our case, residuals are somewhat evenly distributed, indicating violation of homoscedasticity. This suggests our model probabiy be less accurate across the predictor range, but it does not drastically impact the overall model’s reliability. we can conclude that suicide_rate not a good candidate for target value.

4.3.1 Feature selection

Feature selection, also known as variable selection, attribute selection, or variable subset selection, is the process of selecting a subset of relevant features for use in model construction. The goal of feature selection is three-fold:

*Improving Model Performance: When irrelevant or partially relevant features are used to construct a predictive model, the accuracy of the model can be significantly degraded. By selecting only the most relevant features to use in model construction, we can enhance the predictive accuracy of the model.

*Reducing Overfitting: Too many features in the model can lead to overfitting, where the model performs well on the training data but poorly on unseen data. By reducing the number of features, we can make the model more generalizable.

*Enhancing Interpretability: Models with fewer features are simpler and easier to interpret.

Reducing Training Time: Fewer features mean faster training times.

Feature selection methods are intended to reduce the number of input variables to those that are believed to be most useful to a model in order to predict the target variable. Not all features are created equal. Some are relevant to the target variable, some are irrelevant, and some are redundant. Feature selection enables us to focus on the relevant and non-redundant features, increasing our model’s performance and interpretability.

Feature selection methods:

Forward Selection: You start with an empty model and add predictors one by one. In each step, you add the variable that gives the most significant improvement to the model.

Backward Selection: You start with the full model and remove predictors one by one. In each step, you remove the variable that is the least significant.

Mixed Selection: This is a combination of forward and backward selection. You start with an empty model, add variables as in forward selection, but after adding each new variable, the method may also remove variables that do not contribute to the model fit. in this project we use criteria like RSS, adjr2, Mallow’s Cp (cp) and Bayesian Information Criterion (BIC). Residual Sum of Squares (RSS): This is a measure of the discrepancy between the data and an estimation model. A small RSS indicates a tight fit of the model to the data.

Adjusted R-squared (adjr2): It is a modification of R-squared that adjusts for the number of predictors in the model. Unlike R-squared, the adjusted R-squared increases only if the new term enhances the model more than would be expected by chance.

Mallow’s Cp (cp): This criterion attempts to identify a model with a balance between under-fitting and over-fitting. Its ideal value is p (the number of predictors in the model), and a good model is a model where Cp is nearly equal to its p-value.

Bayesian Information Criterion (BIC): This criterion deals with model selection problems. Lower BIC means better model.

log_suicide_ratio

library(leaps)

# First, fit a full model
full_model <- model_log_suicide_ratio

#log_suicide_ratio
formula <- as.formula(paste("scaled_log_suicide_ratio", "~", paste(scaled_log_suicide_ratio_var, collapse = " + ")))



# Forward Selection
forward_model_log <- regsubsets(formula, data = data2, nvmax = length(data2)-1, method = "forward")
Warning: 5  linear dependencies found
Reordering variables and trying again:
forward_summary <- summary(forward_model_log)

# Backward Selection
backward_model_log <- regsubsets(formula, data = data2, nvmax = length(data2)-1, method = "backward")
Warning: 5  linear dependencies found
Reordering variables and trying again:
backward_summary <- summary(backward_model_log)

# Mixed (stepwise) selection
stepwise_model_log <- regsubsets(formula, data = data2, nvmax = length(data2)-1, method = "seqrep")
Warning: 5  linear dependencies found
Reordering variables and trying again:
stepwise_summary <- summary(stepwise_model_log)

# Create a dataframe with the criteria for each method
comparison_df_log <- data.frame(
  Method = c("Forward", "Backward", "Mixed"),
  RSS = c(forward_summary$rss[which.min(forward_summary$cp)], 
          backward_summary$rss[which.min(backward_summary$cp)], 
          stepwise_summary$rss[which.min(stepwise_summary$cp)]),
  AdjustedR2 = c(max(forward_summary$adjr2), max(backward_summary$adjr2), max(stepwise_summary$adjr2)),
  Cp = c(min(forward_summary$cp), min(backward_summary$cp), min(stepwise_summary$cp)),
  BIC = c(min(forward_summary$bic), min(backward_summary$bic), min(stepwise_summary$bic))
)


print(comparison_df_log)
NA

forward has higher adjustedR2 and lower BIC for log.

library(leaps)

# First, fit a full model
full_model <- model_sqrt_suicide_ratio

#log_suicide_ratio
formula <- as.formula(paste("scaled_sqrt_suicide_ratio", "~", paste(scaled_sqrt_suicide_ratio_var, collapse = " + ")))

  

# Forward Selection
forward_model_sqrt <- regsubsets(formula, data = data2, nvmax = length(data2)-1, method = "forward")
Warning: 5  linear dependencies found
Reordering variables and trying again:
forward_summary <- summary(forward_model_sqrt)

# Backward Selection
backward_model_sqrt <- regsubsets(formula, data = data2, nvmax = length(data2)-1, method = "backward")
Warning: 5  linear dependencies found
Reordering variables and trying again:
backward_summary <- summary(backward_model_sqrt)

# Mixed (stepwise) selection
stepwise_model_sqrt <- regsubsets(formula, data = data2, nvmax = length(data2)-1, method = "seqrep")
Warning: 5  linear dependencies found
Reordering variables and trying again:
stepwise_summary <- summary(stepwise_model_sqrt)

# Create a dataframe with the criteria for each method
comparison_df_sqrt <- data.frame(
  Method = c("Forward", "Backward", "Mixed"),
  RSS = c(forward_summary$rss[which.min(forward_summary$cp)], 
          backward_summary$rss[which.min(backward_summary$cp)], 
          stepwise_summary$rss[which.min(stepwise_summary$cp)]),
  AdjustedR2 = c(max(forward_summary$adjr2), max(backward_summary$adjr2), max(stepwise_summary$adjr2)),
  Cp = c(min(forward_summary$cp), min(backward_summary$cp), min(stepwise_summary$cp)),
  BIC = c(min(forward_summary$bic), min(backward_summary$bic), min(stepwise_summary$bic))
)


print(comparison_df_sqrt)
NA

forward has higher adjustedR2 and lower BIC for both log and sqrt but its much lesser than original model

var1<-c(scaled_log_suicide_ratio_var,"scaled_log_suicide_ratio")
data3<-data2 %>% select(one_of(var1))
trainData_log <- data3 %>% filter(year <=2010)
testData_log <- data3 %>% filter(year >2010)
var1<-c(scaled_sqrt_suicide_ratio_var,"scaled_sqrt_suicide_ratio")
data3<-data2 %>% select(one_of(var1))
trainData_sqrt <- data3 %>% filter(year <=2010)
testData_sqrt <- data3 %>% filter(year >2010)

just for seeing how much our works on data is been influential on performance of model we inspect initial data performance on models too

initial_var<-c("country","year","sex","age","suicide_ratio","GDP_for_year","GDP_per_capita","generation","continent","life_exp","avg_temp","max_temp","min_temp")
data3<-data %>% select(one_of(initial_var))
trainData_initial <- data3 %>% filter(year <=2010)
testData_initial <- data3 %>% filter(year >2010)

4.4.1 Simple linear model

first we train a model for target log

library(Metrics)
formula <- as.formula(paste("scaled_log_suicide_ratio", "~", paste(scaled_log_suicide_ratio_var, collapse = " + ")))

model <- lm(formula, data = trainData_log)
data_test <- testData_log
# Make predictions on the testing data
predictions <- predict(model, newdata = testData_log)
Warning: prediction from a rank-deficient fit may be misleading
target <-"scaled_log_suicide_ratio"
# Calculate Mean Squared Error (MSE)
mse <- mse(testData_log$scaled_log_suicide_ratio, predictions)
print(paste0("MSE: ", mse))
[1] "MSE: 0.259606519647617"
# Calculate R-squared
sse = sum((predictions - testData_log$scaled_log_suicide_ratio)^2)
sst = sum((testData_log$scaled_log_suicide_ratio - mean(testData_log$scaled_log_suicide_ratio))^2)
r_squared = 1 - sse / sst
print(paste0("R-squared: ", r_squared))
[1] "R-squared: 0.713841021873424"
# Calculate Adjusted R-squared
n = length(testData_log$scaled_log_suicide_ratio) # number of observations
p = length(coef(model)) - 1 # number of predictors
adjusted_r_squared = 1 - (1 - r_squared) * ((n - 1) / (n - p - 1))
print(paste0("Adjusted R-squared: ", adjusted_r_squared))
[1] "Adjusted R-squared: 0.705274774146334"
library(Metrics)
formula <- as.formula(paste("scaled_sqrt_suicide_ratio", "~", paste(scaled_sqrt_suicide_ratio_var, collapse = " + ")))

model <- lm(formula, data = trainData_sqrt)

# Make predictions on the testing data
predictions <- predict(model, newdata = testData_sqrt)
Warning: prediction from a rank-deficient fit may be misleading
# Calculate Mean Squared Error (MSE)
mse <- mse(testData_sqrt$scaled_sqrt_suicide_ratio, predictions)
print(paste0("MSE: ", mse))
[1] "MSE: 0.278237222189494"
# Calculate R-squared
sse = sum((predictions - testData_sqrt$scaled_sqrt_suicide_ratio)^2)
sst = sum((testData_sqrt$scaled_sqrt_suicide_ratio - mean(testData_sqrt$scaled_sqrt_suicide_ratio))^2)
r_squared = 1 - sse / sst
print(paste0("R-squared: ", r_squared))
[1] "R-squared: 0.657814425097907"
# Calculate Adjusted R-squared
n = length(testData_sqrt$scaled_sqrt_suicide_ratio) # number of observations
p = length(coef(model)) - 1 # number of predictors
adjusted_r_squared = 1 - (1 - r_squared) * ((n - 1) / (n - p - 1))
print(paste0("Adjusted R-squared: ", adjusted_r_squared))
[1] "Adjusted R-squared: 0.647571005784495"

initial_data

library(Metrics)
a <-c("country","year","sex","age","GDP_for_year","GDP_per_capita","generation","continent","life_exp","avg_temp","max_temp","min_temp")
formula <- as.formula(paste("suicide_ratio", "~", paste(a, collapse = " + ")))

model <- lm(formula, data = trainData_initial)

# Make predictions on the testing data
predictions <- predict(model, newdata = testData_initial)
Warning: prediction from a rank-deficient fit may be misleading
# Calculate Mean Squared Error (MSE)
mse <- mse(testData_initial$suicide_ratio, predictions)
print(paste0("MSE: ", mse))
[1] "MSE: 143.188698245645"
# Calculate R-squared
sse = sum((predictions - testData_initial$suicide_ratio)^2)
sst = sum((testData_initial$suicide_ratio - mean(testData_initial$suicide_ratio))^2)
r_squared = 1 - sse / sst
print(paste0("R-squared: ", r_squared))
[1] "R-squared: 0.485732100309933"
# Calculate Adjusted R-squared
n = length(testData_initial$suicide_ratio) # number of observations
p = length(coef(model)) - 1 # number of predictors
adjusted_r_squared = 1 - (1 - r_squared) * ((n - 1) / (n - p - 1))
print(paste0("Adjusted R-squared: ", adjusted_r_squared))
[1] "Adjusted R-squared: 0.470337349267431"

it shows that our hard work was effective ### 4.4.2 Lasso regression model Lasso regression is a type of regression analysis method that performs both variable selection and regularization in order to enhance the prediction accuracy and interpretability of the statistical model it produces. The term Lasso is an acronym for Least Absolute Shrinkage and Selection Operator.

The Lasso method introduces a penalty term to the loss function of the linear regression model that is the absolute value of the magnitude of the coefficient values, or simply the absolute value of each coefficient. lasso for log target


library(caret)
Loading required package: lattice

Attaching package: ‘caret’

The following objects are masked from ‘package:Metrics’:

    precision, recall

The following object is masked from ‘package:purrr’:

    lift
library(glmnet)
Loading required package: Matrix

Attaching package: ‘Matrix’

The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack

Loaded glmnet 4.1-7
x_train <- model.matrix(scaled_log_suicide_ratio~., trainData_log)[,-1] # Exclude intercept column
y_train <- trainData_log$scaled_log_suicide_ratio
x_test <- model.matrix(scaled_log_suicide_ratio~., testData_log)[,-1] # Exclude intercept column
y_test <- testData_log$scaled_log_suicide_ratio

# Define the cross-validation experiment
cvfit <- cv.glmnet(x_train, y_train, alpha = 1, type.measure = "mse")

# Get the optimal lambda value
lambda_optimal <- cvfit$lambda.min

# Train the final model using the optimal lambda
final_model <- glmnet(x_train, y_train, alpha = 1, lambda = lambda_optimal)


predictions <- predict(final_model, s = lambda_optimal, newx = x_test)

# Evaluate the performance
mse <- mean((predictions - y_test)^2)

print(paste("MSE on the test set: ", mse))
[1] "MSE on the test set:  0.260504473396823"
rsq <- 1 - sum((predictions - y_test)^2) / sum((mean(y_test) - y_test)^2)
print(paste("RSQ test :",rsq))
[1] "RSQ test : 0.712851225748016"
# Calculate adjusted R-squared
n <- length(y_test) # number of observations
p <- coef(final_model, s = "lambda.min") # number of predictors
adj_rsq <- 1 - (1 - rsq) * (n - 1) / (n - length(p) - 1)

print(paste("Adjuster R squre :",adj_rsq))
[1] "Adjuster R squre : 0.704095745043625"

for sqre target


library(caret)
library(glmnet)


x_train <- model.matrix(scaled_sqrt_suicide_ratio~., trainData_sqrt)[,-1] # Exclude intercept column
y_train <- trainData_sqrt$scaled_sqrt_suicide_ratio
x_test <- model.matrix(scaled_sqrt_suicide_ratio~., testData_sqrt)[,-1] # Exclude intercept column
y_test <- testData_sqrt$scaled_sqrt_suicide_ratio

# Define the cross-validation experiment
cvfit <- cv.glmnet(x_train, y_train, alpha = 1, type.measure = "mse")

# Get the optimal lambda value
lambda_optimal <- cvfit$lambda.min

# Train the final model using the optimal lambda
final_model <- glmnet(x_train, y_train, alpha = 1, lambda = lambda_optimal)


predictions <- predict(final_model, s = lambda_optimal, newx = x_test)

# Evaluate the performance
mse <- mean((predictions - y_test)^2)

print(paste("MSE on the test set: ", mse))
[1] "MSE on the test set:  0.279968871779737"
rsq <- 1 - sum((predictions - y_test)^2) / sum((mean(y_test) - y_test)^2)
print(paste("RSQ test :",rsq))
[1] "RSQ test : 0.655684783686511"
# Calculate adjusted R-squared
n <- length(y_test) # number of observations
p <- coef(final_model, s = "lambda.min") # number of predictors
adj_rsq <- 1 - (1 - rsq) * (n - 1) / (n - length(p) - 1)

print(paste("Adjuster R squre :",adj_rsq))
[1] "Adjuster R squre : 0.64518623553664"

4.4.3 Ridge regression

Ridge regression, also known as Tikhonov regularization, is a regularization technique designed to deal with multicollinearity, improve prediction accuracy, and interpretability of the statistical model it is applied to. Ridge regression performs “L2 regularization,” which means that it adds a penalty equivalent to the square of the magnitude of the coefficients. This results in smaller coefficients, which makes the model less complex and better at generalizing from the training data to unseen data.

for log target


library(caret)
library(glmnet)


x_train <- model.matrix(scaled_log_suicide_ratio~., trainData_log)[,-1] # Exclude intercept column
y_train <- trainData_log$scaled_log_suicide_ratio
x_test <- model.matrix(scaled_log_suicide_ratio~., testData_log)[,-1] # Exclude intercept column
y_test <- testData_log$scaled_log_suicide_ratio

# Define the cross-validation experiment
cvfit <- cv.glmnet(x_train, y_train, alpha = 0, type.measure = "mse")

# Get the optimal lambda value
lambda_optimal <- cvfit$lambda.min

# Train the final model using the optimal lambda
final_model <- glmnet(x_train, y_train, alpha = 0, lambda = lambda_optimal)


predictions <- predict(final_model, s = lambda_optimal, newx = x_test)

# Evaluate the performance
rmse <-mean((predictions - y_test)^2)

print(paste("MSE on the test set: ", rmse))
[1] "MSE on the test set:  0.261420531902801"
rsq <- 1 - sum((predictions - y_test)^2) / sum((mean(y_test) - y_test)^2)
print(paste("RSQ test :",rsq))
[1] "RSQ test : 0.711841473118035"
# Calculate adjusted R-squared
n <- length(y_test) # number of observations
p <- coef(final_model, s = "lambda.min") # number of predictors
adj_rsq <- 1 - (1 - rsq) * (n - 1) / (n - length(p) - 1)

print(paste("Adjuster R squre :",adj_rsq))
[1] "Adjuster R squre : 0.70305520394975"

for sqrt target

x_train <- model.matrix(scaled_sqrt_suicide_ratio~., trainData_sqrt)[,-1] # Exclude intercept column
y_train <- trainData_sqrt$scaled_sqrt_suicide_ratio
x_test <- model.matrix(scaled_sqrt_suicide_ratio~., testData_sqrt)[,-1] # Exclude intercept column
y_test <- testData_sqrt$scaled_sqrt_suicide_ratio

# Define the cross-validation experiment
cvfit <- cv.glmnet(x_train, y_train, alpha = 0, type.measure = "mse")

# Get the optimal lambda value
lambda_optimal <- cvfit$lambda.min

# Train the final model using the optimal lambda
final_model <- glmnet(x_train, y_train, alpha = 0, lambda = lambda_optimal)


predictions <- predict(final_model, s = lambda_optimal, newx = x_test)

# Evaluate the performance
rmse <- mean((predictions - y_test)^2)

print(paste("MSE on the test set: ", rmse))
[1] "MSE on the test set:  0.275760521328236"
rsq <- 1 - sum((predictions - y_test)^2) / sum((mean(y_test) - y_test)^2)
print(paste("RSQ test :",rsq))
[1] "RSQ test : 0.660860355837873"
# Calculate adjusted R-squared
n <- length(y_test) # number of observations
p <- coef(final_model, s = "lambda.min") # number of predictors
adj_rsq <- 1 - (1 - rsq) * (n - 1) / (n - length(p) - 1)

print(paste("Adjuster R squre :",adj_rsq))
[1] "Adjuster R squre : 0.650519616552844"
vec1 <-c("Simple Linear",0.2596,0.7138,0.7052,"log_suicide_ratio")
vec2<-c("Simple Linear",0.278,0.6578,0.6475,"sqrt_suicide_ratio")
vec3<-c("Simple Linear",143.18,0.485,0.4703,"suicide_ratio")
vec4<-c("Lasso",0.2604,0.7129,0.7042,"log_suicide_ratio")
vec5<-c("Lasso",0.2798,0.655,0.6453,"sqrt_suicide_ratio")
vec6<-c("Ridge",0.2615,"0.7116","0.7028","log_suicide_ratio")
vec7<- c("Ridge",0.2758,0.6607,0.6503,"sqrt_suicide_ratio")

5.Conclusions

df <- data.frame(Model=rep(NA,7), 
                 Mean_Squared_Error=rep(NA,7), 
                 R_squared=rep(NA,7), 
                 Adjusted_R_squared=rep(NA,7), 
                 Target=rep(NA,7))
df[1, ] <- vec1
df[2, ] <- vec2
df[3, ] <- vec3
df[4, ] <- vec4
df[5, ] <- vec5
df[6, ] <- vec6
df[7, ] <- vec7
df

The simple linear regression model with log as the target slightly outperforms the others. However, simple linear, Lasso, and Ridge regressions with log as the target demonstrated quite similar performance. To distinguish more effectively between these models, we should employ cross-validation techniques.

Now, let’s explore the importance of each feature in the simple linear regression model, where the target is ‘log_suicide_ratio’. This will give us more insight into the significant predictors in our model.

library(Metrics)
formula <- as.formula(paste("scaled_log_suicide_ratio", "~", paste(scaled_log_suicide_ratio_var, collapse = " + ")))

model <- lm(formula, data = trainData_log)
data_test <- testData_log
# Make predictions on the testing data
predictions <- predict(model, newdata = testData_log)
Warning: prediction from a rank-deficient fit may be misleading
target <-"scaled_log_suicide_ratio"
# Calculate Mean Squared Error (MSE)
mse <- mse(testData_log$scaled_log_suicide_ratio, predictions)
print(paste0("MSE: ", mse))
[1] "MSE: 0.259606519647617"
# Calculate R-squared
sse = sum((predictions - testData_log$scaled_log_suicide_ratio)^2)
sst = sum((testData_log$scaled_log_suicide_ratio - mean(testData_log$scaled_log_suicide_ratio))^2)
r_squared = 1 - sse / sst
print(paste0("R-squared: ", r_squared))
[1] "R-squared: 0.713841021873424"
# Calculate Adjusted R-squared
n = length(testData_log$scaled_log_suicide_ratio) # number of observations
p = length(coef(model)) - 1 # number of predictors
adjusted_r_squared = 1 - (1 - r_squared) * ((n - 1) / (n - p - 1))
print(paste0("Adjusted R-squared: ", adjusted_r_squared))
[1] "Adjusted R-squared: 0.705274774146334"
summary(model)

Call:
lm(formula = formula, data = trainData_log)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.8964 -0.2737  0.0349  0.2937  2.5030 

Coefficients: (5 not defined because of singularities)
                                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)                          8.6339637  1.9861361   4.347 1.39e-05 ***
year                                -0.0051372  0.0009847  -5.217 1.84e-07 ***
countryAntigua and Barbuda           2.0701317  0.1211419  17.088  < 2e-16 ***
countryArgentina                     1.0611908  0.0789824  13.436  < 2e-16 ***
countryArmenia                      -0.2411279  0.0567482  -4.249 2.16e-05 ***
countryAruba                         2.2125428  0.1356740  16.308  < 2e-16 ***
countryAustralia                     1.3983657  0.0959024  14.581  < 2e-16 ***
countryAustria                       1.4884649  0.0820656  18.137  < 2e-16 ***
countryAzerbaijan                   -0.6248443  0.0518645 -12.048  < 2e-16 ***
countryBahamas                       1.0835249  0.1160726   9.335  < 2e-16 ***
countryBahrain                       1.0136539  0.1238993   8.181 2.99e-16 ***
countryBarbados                      1.1443311  0.1220616   9.375  < 2e-16 ***
countryBelarus                       1.6274245  0.0688722  23.630  < 2e-16 ***
countryBelgium                       1.5312773  0.0699516  21.891  < 2e-16 ***
countryBelize                        1.6748394  0.1085822  15.425  < 2e-16 ***
countryBrazil                        0.6348542  0.2115340   3.001 0.002693 ** 
countryBulgaria                      1.2786532  0.0489592  26.117  < 2e-16 ***
countryCanada                        0.8728859  0.1602775   5.446 5.21e-08 ***
countryChile                         0.6725118  0.0552855  12.164  < 2e-16 ***
countryColombia                      0.6409490  0.1201195   5.336 9.62e-08 ***
countryCosta Rica                    0.8822440  0.1126480   7.832 5.06e-15 ***
countryCroatia                       1.5704023  0.0563754  27.856  < 2e-16 ***
countryCuba                          1.8129338  0.1115448  16.253  < 2e-16 ***
countryCyprus                        0.3776505  0.0901405   4.190 2.81e-05 ***
countryCzech Republic                1.2561592  0.0643312  19.526  < 2e-16 ***
countryDenmark                       0.5409765  0.2418171   2.237 0.025289 *  
countryEcuador                       0.7367235  0.0865224   8.515  < 2e-16 ***
countryEl Salvador                   1.2255700  0.1079683  11.351  < 2e-16 ***
countryEstonia                       1.6255610  0.0788501  20.616  < 2e-16 ***
countryFiji                          1.0003509  0.1138688   8.785  < 2e-16 ***
countryFinland                       1.4448523  0.1079861  13.380  < 2e-16 ***
countryFrance                        1.6970096  0.1047866  16.195  < 2e-16 ***
countryGeorgia                      -0.1103127  0.0587077  -1.879 0.060258 .  
countryGermany                       1.3118352  0.1095124  11.979  < 2e-16 ***
countryGreece                        0.0775424  0.0609030   1.273 0.202958    
countryGrenada                       1.9601590  0.1217486  16.100  < 2e-16 ***
countryGuatemala                     0.1102693  0.0947247   1.164 0.244397    
countryGuyana                        1.9571851  0.1148735  17.038  < 2e-16 ***
countryHungary                       1.7938440  0.0569404  31.504  < 2e-16 ***
countryIceland                       1.2192789  0.1095099  11.134  < 2e-16 ***
countryIreland                       0.9163185  0.0683086  13.414  < 2e-16 ***
countryIsrael                        0.9942509  0.0837859  11.867  < 2e-16 ***
countryItaly                         0.8276146  0.1030944   8.028 1.05e-15 ***
countryJamaica                      -0.7837046  0.1175467  -6.667 2.68e-11 ***
countryJapan                         1.5921159  0.1856974   8.574  < 2e-16 ***
countryKazakhstan                    1.7129881  0.0707859  24.200  < 2e-16 ***
countryKiribati                      2.4574776  0.1241918  19.788  < 2e-16 ***
countryKuwait                        0.4778700  0.1182609   4.041 5.35e-05 ***
countryKyrgyzstan                    0.9504617  0.0840938  11.302  < 2e-16 ***
countryLatvia                        1.6583482  0.0752545  22.037  < 2e-16 ***
countryLithuania                     1.9585877  0.0724760  27.024  < 2e-16 ***
countryLuxembourg                    1.4596508  0.0773800  18.863  < 2e-16 ***
countryMalta                         0.9363929  0.0753984  12.419  < 2e-16 ***
countryMauritius                     1.5074685  0.0983495  15.328  < 2e-16 ***
countryMexico                        0.3567201  0.1164615   3.063 0.002194 ** 
countryMontenegro                    0.4716294  0.0624739   7.549 4.58e-14 ***
countryNetherlands                   0.9901728  0.0704670  14.052  < 2e-16 ***
countryNew Zealand                   1.2284615  0.0635832  19.321  < 2e-16 ***
countryNicaragua                     0.9602198  0.1880365   5.107 3.31e-07 ***
countryNorway                        0.9817333  0.1183265   8.297  < 2e-16 ***
countryPanama                        0.8308842  0.1181729   7.031 2.12e-12 ***
countryParaguay                      0.4410477  0.0962993   4.580 4.68e-06 ***
countryPhilippines                   0.0931614  0.1477824   0.630 0.528443    
countryPoland                        1.2150773  0.0869009  13.982  < 2e-16 ***
countryPortugal                      0.8521113  0.0605372  14.076  < 2e-16 ***
countryPuerto Rico                   0.9889828  0.1165556   8.485  < 2e-16 ***
countryQatar                         1.0969368  0.1374820   7.979 1.56e-15 ***
countryRepublic of Korea             1.6383170  0.0823173  19.902  < 2e-16 ***
countryRomania                       1.0464677  0.0714912  14.638  < 2e-16 ***
countryRussian Federation            1.5026077  0.2184321   6.879 6.21e-12 ***
countrySaint Lucia                   1.8441775  0.1207396  15.274  < 2e-16 ***
countrySaint Vincent and Grenadines  2.0450841  0.1219296  16.773  < 2e-16 ***
countrySerbia                        1.4364946  0.0583010  24.639  < 2e-16 ***
countrySeychelles                    2.2898435  0.1246346  18.372  < 2e-16 ***
countrySingapore                     1.7089927  0.1255121  13.616  < 2e-16 ***
countrySlovakia                      0.6189721  0.0653224   9.476  < 2e-16 ***
countrySlovenia                      1.7136539  0.0664696  25.781  < 2e-16 ***
countrySouth Africa                 -0.8752966  0.0889033  -9.845  < 2e-16 ***
countrySpain                         0.9397352  0.0855540  10.984  < 2e-16 ***
countrySri Lanka                     2.2730792  0.1260424  18.034  < 2e-16 ***
countrySuriname                      2.0132034  0.1165673  17.271  < 2e-16 ***
countrySweden                        1.1350470  0.1022006  11.106  < 2e-16 ***
countrySwitzerland                   1.4597035  0.0856385  17.045  < 2e-16 ***
countryThailand                      1.1267797  0.1421507   7.927 2.38e-15 ***
countryTrinidad and Tobago           1.6136069  0.1173823  13.747  < 2e-16 ***
countryTurkey                       -0.1398765  0.1418454  -0.986 0.324087    
countryTurkmenistan                  0.9692461  0.0502468  19.290  < 2e-16 ***
countryUkraine                       1.6383462  0.0857071  19.116  < 2e-16 ***
countryUnited Arab Emirates          0.3837009  0.1467405   2.615 0.008934 ** 
countryUnited Kingdom                0.7563182  0.1081298   6.995 2.75e-12 ***
countryUnited States                 0.9927339  0.1865643   5.321 1.04e-07 ***
countryUruguay                       1.4711658  0.0644911  22.812  < 2e-16 ***
countryUzbekistan                    0.7692677  0.0514785  14.943  < 2e-16 ***
sexMale                              1.0290630  0.0070749 145.453  < 2e-16 ***
age.L                                0.6570340  0.0079100  83.064  < 2e-16 ***
age.Q                                0.1526387  0.0079100  19.297  < 2e-16 ***
age.C                                0.1283105  0.0079100  16.221  < 2e-16 ***
age^4                                0.0089594  0.0079100   1.133 0.257367    
continentAmericas                           NA         NA      NA       NA    
continentAsia                               NA         NA      NA       NA    
continentEurope                             NA         NA      NA       NA    
continentOceania                            NA         NA      NA       NA    
population_bine_jenks.L              0.0330934  0.1321995   0.250 0.802335    
population_bine_jenks.Q              0.1695602  0.0827496   2.049 0.040468 *  
population_bine_jenks.C                     NA         NA      NA       NA    
scaled_log_GDP_capita               -0.0605127  0.0181248  -3.339 0.000843 ***
min_temp_bine_jenks.L                0.0011148  0.0201592   0.055 0.955898    
min_temp_bine_jenks.Q               -0.0054622  0.0116370  -0.469 0.638801    
gdp_per_capita_bine_jenks.L          0.0617656  0.0315224   1.959 0.050079 .  
gdp_per_capita_bine_jenks.Q         -0.0241925  0.0174193  -1.389 0.164900    
gdp_per_capita_bine_jenks.C          0.0089384  0.0130658   0.684 0.493914    
scaled_avg_temp                     -0.2252791  0.0713174  -3.159 0.001587 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4875 on 18883 degrees of freedom
Multiple R-squared:  0.7674,    Adjusted R-squared:  0.7661 
F-statistic: 587.6 on 106 and 18883 DF,  p-value: < 2.2e-16

as we can see the most important feature in order are:

  1. sex
  2. country
  3. age
  4. scaled_avg_temp
  5. scaled_log_GDP_capita
  6. population_bine_jenks
LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgTGVhcm5pbmciCmF1dGhvcjogIjxpPkFtaW4gQWxtYXNpLCBBc21hIEhvc2VpbnBvdXIgPC9pPiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB0cnVlCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKCmBgYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKSAjIGdlbmVyYWwKbGlicmFyeShnZ2FsdCkgIyBkdW1iYmVsbCBwbG90cwpsaWJyYXJ5KGdyaWQpICMgcGxvdHMKbGlicmFyeShncmlkRXh0cmEpICMgcGxvdHMKbGlicmFyeShnZ2NvcnJwbG90KSAKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJ3b3JsZG1hcCkgIyBxdWljayBjb3VudHJ5LWxldmVsIGhlYXQgbWFwCmxpYnJhcnkoY291bnRyeWNvZGUpICMgY29udGluZW50CmxpYnJhcnkoYnJvb20pICMgc2lnbmlmaWNhbnQgdHJlbmRzIHdpdGhpbiBjb3VudHJpZXMKbGlicmFyeShsdWJyaWRhdGUpICMgZGVhbGluZyB3aXRoIGRhdGVzCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KHNjYWxlcykKYGBgCgojIDEuT2J0YWluaW5nIERhdGEgCgojIyAxLjEgU3VpY2lkZSBEYXRhCgpUaGUgZGF0YXNldCB3YXMgc291cmNlZCBmcm9tIFtsaW5rc10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9ydXNzZWxseWF0ZXM4OC9zdWljaWRlLXJhdGVzLW92ZXJ2aWV3LTE5ODUtdG8tMjAxNj9kYXRhc2V0SWQ9ODUzNTEmc29ydEJ5PXZvdGVDb3VudCZsYW5ndWFnZT1SKSBhbmQgY29udGFpbnMgYSBjb21wcmVoZW5zaXZlIGNvbGxlY3Rpb24gb2Ygc3VpY2lkZSBzdGF0aXN0aWNzIGJyb2tlbiBkb3duIGJ5IGNvdW50cnksIHllYXIsIGFnZSwgYW5kIHNleC4gVGhpcyBkYXRhIGhhcyBiZWVuIGdhdGhlcmVkIGZyb20gbXVsdGlwbGUgZGF0YXNldHMgd2l0aCB0aGUgaW50ZW50aW9uIG9mIGlkZW50aWZ5aW5nIHRyZW5kcyBhbmQgcGF0dGVybnMgaW4gZ2xvYmFsIHN1aWNpZGUgcmF0ZXMuIEhlcmUgYXJlIHRoZSBmZWF0dXJlcyBvZiB0aGlzIGRhdGFzZXQ6CgoqICoqQ291bnRyeSoqOiBUaGUgY291bnRyeSB3aGVyZSB0aGUgZGF0YSB3YXMgcmVjb3JkZWQuCgoqICoqWWVhcioqOiBUaGUgeWVhciB3aGVuIHRoZSBkYXRhIHdhcyByZWNvcmRlZC4gVGhlIGRhdGEgd2FzIGNvbGxlY3QgZnJvbSAxOTg1IHVudGlsIDIwMTYuCgoqICoqU2V4Kio6IFRoZSBzZXggb2YgdGhlIGluZGl2aWR1YWxzIGluY2x1ZGVkIGluIHRoZSBzdWljaWRlIGNvdW50LgoKKiAqKkFnZSoqOiBUaGUgYWdlIGdyb3VwIG9mIHRoZSBpbmRpdmlkdWFscyBpbmNsdWRlZCBpbiB0aGUgc3VpY2lkZSBjb3VudC4KCiogKipTdWljaWRlX25vKio6IFRoZSB0b3RhbCBudW1iZXIgb2Ygc3VpY2lkZXMgcmVjb3JkZWQgaW4gYSBzcGVjaWZpYyBjb3VudHJ5LCB5ZWFyLCBhZ2UgZ3JvdXAsIGFuZCBzZXguCgoqICoqUG9wdWxhdGlvbioqOiBUaGUgdG90YWwgcG9wdWxhdGlvbiBvZiB0aGUgc3BlY2lmaWMgYWdlIGFuZCBzZXggZ3JvdXAgaW4gdGhlIGNvdW50cnkgZm9yIHRoYXQgeWVhci4KCiogKipTdWljaWRlLyAxMDBrIHBvcCoqOiBUaGlzIGlzIGEgZGVyaXZlZCBtZXRyaWMgcmVwcmVzZW50aW5nIHRoZSBudW1iZXIgb2Ygc3VpY2lkZXMgcGVyIDEwMCwwMDAgcGVvcGxlIGluIHRoZSBwb3B1bGF0aW9uLCBjYWxjdWxhdGVkIGFzIChTdWljaWRlX25vIC8gUG9wdWxhdGlvbikgKiAxMDAwMDAuCgoqICoqSERJX2Zvcl95ZWFyKio6IEh1bWFuIERldmVsb3BtZW50IEluZGV4LCBhIHN0YXRpc3RpYyBjb21wb3NpdGUgaW5kZXggb2YgbGlmZSBleHBlY3RhbmN5LCBlZHVjYXRpb24sIGFuZCBwZXIgY2FwaXRhIGluY29tZSBpbmRpY2F0b3JzLgoKKiAqKkdEUF9mb3JfeWVhciAoJCkqKjogVGhlIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgKEdEUCkgb2YgdGhlIGNvdW50cnkgZm9yIHRoZSBzcGVjaWZpZWQgeWVhciwgbWVhc3VyZWQgaW4gVVMgZG9sbGFycy4KCiogKipHRFBfcGVyX2NhcGl0YSAoXCQpKio6IFRoZSBHRFAgcGVyIGNhcGl0YSBvZiB0aGUgY291bnRyeSBmb3IgdGhlIHNwZWNpZmllZCB5ZWFyLCBhbHNvIG1lYXN1cmVkIGluIFVTIGRvbGxhcnMuIEl0IGlzIGNhbGN1bGF0ZWQgYnkgZGl2aWRpbmcgdGhlIEdEUF9mb3JfeWVhciAoJCkgYnkgdGhlIHRvdGFsIHBvcHVsYXRpb24gb2YgdGhlIGNvdW50cnkuCgoqICoqR2VuZXJhdGlvbioqOiBUaGUgZ2VuZXJhdGlvbiBjb2hvcnQgb2YgdGhlIGluZGl2aWR1YWxzIGluY2x1ZGVkIGluIHRoZSBzdWljaWRlIGNvdW50IChlLmcuLCBHZW4gWCwgQm9vbWVycywgZXRjLikuCgpgYGB7cn0Kc3VpY2lkZV9kYXRhIDwtIHJlYWRfY3N2KCJzdWljaWRlX2RhdGEuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKaGVhZChzdWljaWRlX2RhdGEpCmBgYApgYGB7cn0KZGltKHN1aWNpZGVfZGF0YSkKYGBgCldlIGZ1cnRoZXIgc3VwcGxlbWVudGVkIG91ciBhbmFseXNpcyBieSBpbmNvcnBvcmF0aW5nIGFkZGl0aW9uYWwgZmVhdHVyZXMsIHdoaWNoIHdlIGh5cG90aGVzaXplIG1heSBzaWduaWZpY2FudGx5IGltcGFjdCBzdWljaWRlIHJhdGVzLgoKIyMgMS4yIENvbnRpbmV0CgpXZSd2ZSBlbnJpY2hlZCBvdXIgZGF0YXNldCBieSBhcHBlbmRpbmcgYSAnY29udGluZW50JyBmZWF0dXJlIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCBjb3VudHJ5LiBUaGlzIGVuaGFuY2VtZW50LCBhY2NvbXBsaXNoZWQgdXRpbGl6aW5nIHRoZSAnY291bnRyeWNvZGUnIGxpYnJhcnksIHdpbGwgZmFjaWxpdGF0ZSBzdWJzZXF1ZW50IGdlb2dyYXBoaWNhbCBhbmFseXNlcy4KCmBgYHtyfQojIGdldHRpbmcgY29udGluZW50IGRhdGE6CnN1aWNpZGVfZGF0YSRjb250aW5lbnQgPC0gY291bnRyeWNvZGUoc291cmNldmFyID0gc3VpY2lkZV9kYXRhJGNvdW50cnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWdpbiA9ICJjb3VudHJ5Lm5hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXN0aW5hdGlvbiA9ICJjb250aW5lbnQiKQoKZGltKHN1aWNpZGVfZGF0YSkKYGBgCgoKIyMgMS4zIExpZmUgRXhwZWN0YW5jeQoKV2UgYWxzbyBhZGRlZCAnbGlmZSBleHBlY3RhbmN5JyBkYXRhIGZyb20gW2xpbmtzXShodHRwczovL2RhdGFiYW5rLndvcmxkYmFuay5vcmcvKS4gUmVtZW1iZXIsIHRoZSBIdW1hbiBEZXZlbG9wbWVudCBJbmRleCAoSERJKSBhbHNvIG1lYXN1cmVzIGxpZmUgZXhwZWN0YW5jeSwgc28gdGhlc2UgdHdvIGZlYXR1cmVzIG1pZ2h0IGJlIGhpZ2hseSBjb3JyZWxhdGVkLiBXZSdsbCBrZWVwIHRoaXMgaW4gbWluZCBmb3Igb3VyIGFuYWx5c2lzLgoKYGBge3J9CmxpZmVfZXhwX2RhdGEgPC1yZWFkX2NzdignbGlmZV9leHAuY3N2JyxzaG93X2NvbF90eXBlcyA9IEZBTFNFKQpoZWFkKGxpZmVfZXhwX2RhdGEpCmBgYAoKCmBgYHtyfQpsaWZlX2V4cF9kYXRhJFRpbWUgPC0gYXMuaW50ZWdlcihsaWZlX2V4cF9kYXRhJFRpbWUpCgpsaWZlX2V4cF9kYXRhIDwtIGxpZmVfZXhwX2RhdGEgJT4lCiAgcmVuYW1lKCBsaWZlX2V4cCA9IGBWYWx1ZWAsCiAgICAgICAgICB5ZWFyID0gYFRpbWVgLAogICAgICAgICAgY291bnRyeSA9IGBDb3VudHJ5IE5hbWVgKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKYGBgCgpXZSd2ZSBpZGVudGlmaWVkIHRoYXQgdGhlIGZvcm1hdCBvZiBzb21lIGNvdW50cnkgbmFtZXMgZGlmZmVycyBiZXR3ZWVuIHRoZSBzdWljaWRlIGRhdGEgYW5kIHRoZSBsaWZlIGV4cGVjdGFuY3kgZGF0YXNldC4gVG8gcHJldmVudCBhbnkgZnVydGhlciBkaXNjcmVwYW5jaWVzLCBpdCdzIGltcGVyYXRpdmUgdGhhdCB3ZSBhZGRyZXNzIGFuZCByZWN0aWZ5IHRoZXNlIGluY29uc2lzdGVuY2llcy4KCmBgYHtyfQpuYW1lX2NoYW5nZXNfbGlmZSA8LSBjKCJCYWhhbWFzLCBUaGUiID0gIkJhaGFtYXMiLCAKICAgICAgICAgICAgICAgICAgIkN6ZWNoaWEiID0gIkN6ZWNoIFJlcHVibGljIiwKICAgICAgICAgICAgICAgICAgIkt5cmd5eiBSZXB1YmxpYyIgPSAiS3lyZ3l6c3RhbiIsCiAgICAgICAgICAgICAgICAgICJNYWNhbyBTQVIsIENoaW5hIiA9ICJNYWNhdSIsCiAgICAgICAgICAgICAgICAgICJLb3JlYSwgUmVwLiIgPSAiUmVwdWJsaWMgb2YgS29yZWEiLAogICAgICAgICAgICAgICAgICAiU3QuIEtpdHRzIGFuZCBOZXZpcyIgPSAiU2FpbnQgS2l0dHMgYW5kIE5ldmlzIiwKICAgICAgICAgICAgICAgICAgIlN0LiBMdWNpYSIgPSAiU2FpbnQgTHVjaWEiLAogICAgICAgICAgICAgICAgICAiU3QuIFZpbmNlbnQgYW5kIHRoZSBHcmVuYWRpbmVzIiA9ICJTYWludCBWaW5jZW50IGFuZCBHcmVuYWRpbmVzIiwKICAgICAgICAgICAgICAgICAgIlNsb3ZhayBSZXB1YmxpYyIgPSAiU2xvdmFraWEiLAogICAgICAgICAgICAgICAgICAiVHVya2l5ZSIgPSAiVHVya2V5IgogICAgICAgICAgICAgICAgICApCgpsaWZlX2V4cF9kYXRhJGNvdW50cnlbbGlmZV9leHBfZGF0YSRjb3VudHJ5ICVpbiUgbmFtZXMobmFtZV9jaGFuZ2VzX2xpZmUpXSA8LSAKICAgIG5hbWVfY2hhbmdlc19saWZlW2xpZmVfZXhwX2RhdGEkY291bnRyeVtsaWZlX2V4cF9kYXRhJGNvdW50cnkgJWluJSBuYW1lcyhuYW1lX2NoYW5nZXNfbGlmZSldXQpgYGAKCmBgYHtyfQpkYXRhIDwtIHN1aWNpZGVfZGF0YSAlPiUKICBsZWZ0X2pvaW4obGlmZV9leHBfZGF0YVssIGMoJ3llYXInLCAnY291bnRyeScsICdsaWZlX2V4cCcpXSwgCiAgICAgICAgICAgIGJ5ID0gYygneWVhcicsICdjb3VudHJ5JykpCmBgYAoKCmBgYHtyfQpkaW0oZGF0YSkKYGBgCiMjIDEuNCBUZW1wZXJhdHVyZQoKT3VyIGludmVzdGlnYXRpb24gc3VnZ2VzdHMgdGhhdCB0ZW1wZXJhdHVyZSBtaWdodCBpbmZsdWVuY2Ugc3VpY2lkZSByYXRlcy4oc291cmNlOiBbbGlua3NdKGh0dHBzOi8vYW5uYWxzLWdlbmVyYWwtcHN5Y2hpYXRyeS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMjk5MS0wMTYtMDEwNi0yI3JlZi1DUjQ3KSkgU28sIHdlIGluY29ycG9yYXRlZCB0ZW1wZXJhdHVyZSBkYXRhIGludG8gb3VyIGRhdGFzZXQgZnJvbSBbbGlua10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9iZXJrZWxleWVhcnRoL2NsaW1hdGUtY2hhbmdlLWVhcnRoLXN1cmZhY2UtdGVtcGVyYXR1cmUtZGF0YT9zb3J0PXZvdGVzKSB0byBmdXJ0aGVyIGV4cGxvcmUgdGhpcyBwb3NzaWJpbGl0eS4gVGhpcyBkYXRhc2V0IGNvdmVycyB0ZW1wZXJhdHVyZSBkYXRhIHNpbmNlIDE3NTAgdGlsbCAyMDEzLiBXZSBpbmNsdWRlIHRocmVlIGFkZGl0aW9uYWwgZmVhdHVyZXM6CgoqICoqbWF4X3RlbXAqKjogVGhlIGhpZ2hlc3QgbW9udGhseSB0ZW1wZXJhdHVyZSBmb3IgZWFjaCB5ZWFyLgoqICoqbWluX3RlbXAqKjogVGhlIGxvd2VzdCBtb250aGx5IHRlbXBlcmF0dXJlIGZvciBlYWNoIHllYXIuCiogKiphdmdfdGVtcCoqOiBUaGUgYXZlcmFnZSBtb250aGx5IHRlbXBlcmF0dXJlIGZvciBlYWNoIHllYXIuCgpgYGB7cn0KdGVtcF9kYXRhIDwtIHJlYWRfY3N2KCJHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdiIsc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKaGVhZCh0ZW1wX2RhdGEpCmBgYAoKYGBge3J9CiMgRXh0cmFjdCB5ZWFyIGFuZCBtb250aCBmcm9tIGRhdGVfY29sdW1uCnRlbXBfZGF0YSR5ZWFyIDwtIHllYXIodGVtcF9kYXRhJGR0KQp0ZW1wX2RhdGEkbW9udGggPC0gbW9udGgodGVtcF9kYXRhJGR0KQoKdGVtcF9kYXRhIDwtIHNlbGVjdCh0ZW1wX2RhdGEsIC1kdCwgLSJBdmVyYWdlVGVtcGVyYXR1cmVVbmNlcnRhaW50eSIpCnRlbXBfZGF0YTwtIHJlbmFtZSh0ZW1wX2RhdGEsIGNvdW50cnkgPSBDb3VudHJ5KQoKdGVtcF9kYXRhJHllYXIgPC1hcy5pbnRlZ2VyKHRlbXBfZGF0YSR5ZWFyKQp0ZW1wX2RhdGEkbW9udGggPC1hcy5pbnRlZ2VyKHRlbXBfZGF0YSRtb250aCkKdGVtcF9kYXRhJEF2ZXJhZ2VUZW1wZXJhdHVyZSA8LWFzLm51bWVyaWModGVtcF9kYXRhJEF2ZXJhZ2VUZW1wZXJhdHVyZSkKCnRlbXBfZGF0YSA8LSB0ZW1wX2RhdGEgJT4lCiAgZmlsdGVyKHllYXIgPj0gMTk4NSAmIHllYXIgPD0gMjAxNikKCnRlbXBfZGF0YSA8LSB0ZW1wX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSwgeWVhcikgJT4lCiAgbXV0YXRlKAogICAgYXZnX3RlbXAgPSBzdW0oQXZlcmFnZVRlbXBlcmF0dXJlLCBuYS5ybSA9IFRSVUUpIC8gc3VtKCFpcy5uYShBdmVyYWdlVGVtcGVyYXR1cmUpKSwKICAgIG1heF90ZW1wID0gbWF4KEF2ZXJhZ2VUZW1wZXJhdHVyZSwgbmEucm0gPSBUUlVFKSwKICAgIG1pbl90ZW1wID0gbWluKEF2ZXJhZ2VUZW1wZXJhdHVyZSwgbmEucm0gPSBUUlVFKQogICkgJT4lCiAgdW5ncm91cCgpCgp0ZW1wX2RhdGEgPC0gc2VsZWN0KHRlbXBfZGF0YSwgLW1vbnRoLCAtIkF2ZXJhZ2VUZW1wZXJhdHVyZSIpCgp0ZW1wX2RhdGEgPC1kaXN0aW5jdCh0ZW1wX2RhdGEpCgp0ZW1wX2RhdGEgPC0gdGVtcF9kYXRhICU+JQogIGZpbHRlcl9hbGwoYWxsX3ZhcnMoIWlzLmluZmluaXRlKC4pKSkKCmBgYAoKQXMgd2l0aCB0aGUgbGlmZSBleHBlY3RhbmN5IGRhdGEsIHdlJ3ZlIG5vdGljZWQgZGlzY3JlcGFuY2llcyBpbiB0aGUgZm9ybWF0dGluZyBvZiBjb3VudHJ5IG5hbWVzIGJldHdlZW4gdGhlIHN1aWNpZGUgZGF0YSBhbmQgdGVtcGVyYXR1cmUgZGF0YXNldC4gVG8gYXZvaWQgYW55IHBvdGVudGlhbCBpc3N1ZXMgZG93biB0aGUgbGluZSwgaXQncyBjcnVjaWFsIHRoYXQgd2UgcmVjb25jaWxlIHRoZXNlIGluY29uc2lzdGVuY2llcyBhbmQgc3RhbmRhcmRpemUgdGhlIGNvdW50cnkgbmFtZXMgYWNyb3NzIGFsbCBkYXRhc2V0cy4KCmBgYHtyfQpuYW1lX2NoYW5nZXNfdGVtcCA8LSBjKCJBbnRpZ3VhIEFuZCBCYXJidWRhIiA9ICJBbnRpZ3VhIGFuZCBCYXJidWRhIiwgCiAgICAgICAgICAgICAgICAgICJCb3NuaWEgQW5kIEhlcnplZ292aW5hIiA9ICJCb3NuaWEgYW5kIEhlcnplZ292aW5hIiwKICAgICAgICAgICAgICAgICAgIlNvdXRoIEtvcmVhIiA9ICJSZXB1YmxpYyBvZiBLb3JlYSIsCiAgICAgICAgICAgICAgICAgICJSdXNzaWEiID0gIlJ1c3NpYW4gRmVkZXJhdGlvbiIsCiAgICAgICAgICAgICAgICAgICJTYWludCBLaXR0cyBBbmQgTmV2aXMiID0gIlNhaW50IEtpdHRzIGFuZCBOZXZpcyIsCiAgICAgICAgICAgICAgICAgICJUcmluaWRhZCBBbmQgVG9iYWdvIiA9ICJUcmluaWRhZCBhbmQgVG9iYWdvIiwKICAgICAgICAgICAgICAgICAgIlNhaW50IFZpbmNlbnQgQW5kIFRoZSBHcmVuYWRpbmVzIiA9ICJTYWludCBWaW5jZW50IGFuZCBHcmVuYWRpbmVzIgogICAgICAgICAgICAgICAgICApCnRlbXBfZGF0YSRjb3VudHJ5W3RlbXBfZGF0YSRjb3VudHJ5ICVpbiUgbmFtZXMobmFtZV9jaGFuZ2VzX3RlbXApXSA8LSAKICAgIG5hbWVfY2hhbmdlc190ZW1wW3RlbXBfZGF0YSRjb3VudHJ5W3RlbXBfZGF0YSRjb3VudHJ5ICVpbiUgbmFtZXMobmFtZV9jaGFuZ2VzX3RlbXApXV0KYGBgCgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JQogIGxlZnRfam9pbih0ZW1wX2RhdGFbLCBjKCd5ZWFyJywgJ2NvdW50cnknLCAnYXZnX3RlbXAnLCAnbWF4X3RlbXAnLCAnbWluX3RlbXAnKV0sIAogICAgICAgICAgICBieSA9IGMoJ3llYXInLCAnY291bnRyeScpKQpgYGAKCmBgYHtyfQpkaW0oZGF0YSkKYGBgCk5vdyB0aGF0IHdlJ3ZlIGNvbGxlY3RlZCBhbGwgdGhlIG5lY2Vzc2FyeSBkYXRhLCBvdXIgbmV4dCBzdGVwIGlzIHRvIGNsZWFuIGFuZCBwcmVwcm9jZXNzIHRoaXMgZGF0YSBmb3IgZnVydGhlciBhbmFseXNpcy4KCgojIDIuQ2xlYW4gYW5kIEZpbHRlciBEYXRhCgpgYGB7cn0KZ2xpbXBzZShoZWFkKGRhdGEsIDgpKQpgYGAKYGBge3J9CnByaW50KGNvbG5hbWVzKGRhdGEpKQpgYGAKCmBgYHtyfQpzYXBwbHkoZGF0YSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4KSkpCmBgYAojIyAyLjEgQ29sdW1ucyBhbmQgVmFsdWVzCgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JSAKICByZW5hbWUoc3VpY2lkZV9yYXRpbyA9IGBzdWljaWRlcy8xMDBrIHBvcGAsIAogICAgICAgICBjb3VudHJ5X3llYXIgPSBgY291bnRyeS15ZWFyYCwKICAgICAgICAgSERJX2Zvcl95ZWFyID0gYEhESSBmb3IgeWVhcmAsCiAgICAgICAgIEdEUF9mb3JfeWVhciA9IGBnZHBfZm9yX3llYXIgKCQpYCwgCiAgICAgICAgIEdEUF9wZXJfY2FwaXRhID0gYGdkcF9wZXJfY2FwaXRhICgkKWApICU+JQogIGFzLmRhdGEuZnJhbWUoKQoKZGF0YSRhZ2UgPC0gZ3N1YigiIHllYXJzIiwgIiIsIGRhdGEkYWdlKQoKZGF0YSRzZXggPC0gaWZlbHNlKGRhdGEkc2V4ID09ICJtYWxlIiwgIk1hbGUiLCAiRmVtYWxlIikKYGBgCgojIyAyLjIgTWlzc2luZyBWYWx1ZXMKCiMjIyAyLjIuMSBEYXRhIFNjYXJjaXR5IGJ5IENvdW50cnkvWWVhcgoKSW4gYW4gaWRlYWwgZGF0YXNldCwgZXZlcnkgdW5pcXVlIGNvbWJpbmF0aW9uIG9mIGNvdW50cnkgYW5kIHllYXIgKGNvdW50cnlfeWVhcikgd291bGQgYmUgcmVwcmVzZW50ZWQgYnkgMTIgZW50cmllcyAoMiBnZW5kZXJzIGFjcm9zcyA2IGFnZSBncm91cHMpLiBOb3csIHdlIG5lZWQgdG8gdmVyaWZ5IHRoZSBjb21wbGV0ZW5lc3Mgb2Ygb3VyIGRhdGEgZm9yIGVhY2ggY291bnRyeV95ZWFyIGNvbWJpbmF0aW9uLgoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeV95ZWFyKSAlPiUKICBjb3VudCgpICU+JSAjdGhpcyBTSE9VTEQgZ2l2ZSAxMiByb3dzIGZvciBldmVyeSBjb3VudHkteWVhciBjb21iaW5hdGlvbiAoNiBhZ2UgYmFuZHMgKiAyIGdlbmRlcikKICBmaWx0ZXIobiAhPSAxMikKYGBgCkl0IGFwcGVhcnMgdGhhdCB0aGVyZSBpcyBwcm9ibGVtIHdpdGggMjAxNiBkYXRhLgoKYGBge3J9CnllYXJfdmFsdWVfY291bnRzIDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShkYXRhJHllYXIpLCBkZWNyZWFzaW5nID0gRkFMU0UpKQpuYW1lcyh5ZWFyX3ZhbHVlX2NvdW50cykgPC0gYygiWWVhciIsICJDb3VudCIpCmhlYWQoeWVhcl92YWx1ZV9jb3VudHMsNSkKYGBgCk91ciBleHBsb3JhdGlvbiByZXZlYWxzIHRoYXQgdGhlIGRhdGFzZXQgZm9yIHRoZSB5ZWFyIDIwMTYgaXMgbm90IG9ubHkgc3BhcnNlLCBidXQgYWxzbyBpbmNvbXBsZXRlIGZvciB0aGUgZmV3IGNvdW50cmllcyB0aGF0IGhhdmUgZW50cmllcy4gQWRkaXRpb25hbGx5LCBkYXRhIGZvciB0aGUgeWVhcnMgYmV0d2VlbiAxOTg1IGFuZCAxOTg5IGFyZSBhbHNvIHF1aXRlIGxpbWl0ZWQuIFRoZXNlIGlzc3VlcyBuZWVkIHRvIGJlIGFkZHJlc3NlZC4KQXMgYSBzb2x1dGlvbiwgd2UndmUgZGVjaWRlZCB0byBleGNsdWRlIHRoZSBkYXRhIGZyb20gMjAxNi4gV2UgYWxzbyBkcm9wIHRoZSAnY291bnRyeV95ZWFyJyBjb2x1bW4gZnJvbSBvdXIgZGF0YXNldC4KCmBgYHtyfQpkYXRhIDwtIGRhdGEgJT4lCiAgZmlsdGVyKHllYXIgIT0gMjAxNikgJT4lICMgSSB0aGVyZWZvcmUgZXhjbHVkZSAyMDE2IGRhdGEKICBzZWxlY3QoLWNvdW50cnlfeWVhcikKYGBgCgpJbiB0aGUgZm9sbG93aW5nIHN0ZXAsIHdlIGZvY3VzIG9uIGZpbHRlcmluZyBvdXIgZGF0YXNldCB0byBlbnN1cmUgaXRzIHJvYnVzdG5lc3MgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIFNwZWNpZmljYWxseSwgd2UgYXJlIGFkZHJlc3NpbmcgdGhlIGlzc3VlIG9mIGNlcnRhaW4gY291bnRyaWVzIHRoYXQgaGF2ZSBpbnN1ZmZpY2llbnQgZGF0YSBzcHJlYWQgYWNyb3NzIHRoZSB5ZWFycy4gVGhlc2Ugc3BhcnNlIGRhdGEgcG9pbnRzIGNhbiBwb3RlbnRpYWxseSBza2V3IG91ciBhbmFseXNpcyBvciBnZW5lcmF0ZSBpbmFjY3VyYXRlIGluc2lnaHRzLiBUaGVyZWZvcmUsIHdlIHdpbGwgc3lzdGVtYXRpY2FsbHkgcmVtb3ZlIHN1Y2ggY291bnRyaWVzIGZyb20gb3VyIGRhdGFzZXQgdG8gbWFpbnRhaW4gZGF0YSBpbnRlZ3JpdHkgYW5kIHJlbGlhYmlsaXR5IGZvciBzdWJzZXF1ZW50IHN0ZXBzIGluIG91ciBzdHVkeS4KCmBgYHtyfQptaW5pbXVtX3llYXJzIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSkgJT4lCiAgc3VtbWFyaXplKHJvd3MgPSBuKCksIAogICAgICAgICAgICB5ZWFycyA9IHJvd3MgLyAxMikgJT4lCiAgYXJyYW5nZSh5ZWFycykKCm1pbmltdW1feWVhcnMgPC0gbWluaW11bV95ZWFycyAlPiUKICBmaWx0ZXIobWluaW11bV95ZWFycyR5ZWFyczw9MykKICAKCmRhdGEgPC0gZGF0YSAlPiUKZmlsdGVyKCEoY291bnRyeSAlaW4lIG1pbmltdW1feWVhcnMkY291bnRyeSkpCgpkaW0oZGF0YSkKYGBgCgpgYGB7cn0Kc2FwcGx5KGRhdGEsIGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeCkpKQpgYGAKV2UndmUgZnVydGhlciByZWZpbmVkIG91ciBkYXRhc2V0LCBlbGltaW5hdGluZyBkYXRhIGZyb20gMjAxNiBhbmQgZnJvbSBlaWdodCBjb3VudHJpZXMgZHVlIHRvIHRoZWlyIHNwYXJzZSBvciBpbmNvbXBsZXRlIGRhdGEuIFRoaXMgZW5zdXJlcyBhIG1vcmUgcmVsaWFibGUgYmFzaXMgZm9yIG91ciBhbmFseXNpcwoKIyMjIDIuMi4yIE5BIFZhbHVlcyAKCk9uY2Ugd2UndmUgZWxpbWluYXRlZCB0aGUgaW5jb21wbGV0ZSBkYXRhLCB3ZSdsbCBwcm9jZWVkIHRvIGluc3BlY3QgZWFjaCBmZWF0dXJlIGZvciB0aGUgcHJlc2VuY2Ugb2YgbnVsbCB2YWx1ZXMuCgpgYGB7cn0KbmFfY291bnRzIDwtIHNhcHBseShkYXRhLCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpL25yb3coZGF0YSkqMTAwKQpuYV9jb3VudHNfZGYgPC0gZGF0YS5mcmFtZShGZWF0dXJlID0gbmFtZXMobmFfY291bnRzKSwgTkFfcmF0aW8gPSBuYV9jb3VudHMpCm5hX2NvdW50c19kZiA9IG5hX2NvdW50c19kZiAlPiUgYHJvd25hbWVzPC1gKCBOVUxMICkKcHJpbnQobmFfY291bnRzX2RmKQpgYGAKCkFwcHJveGltYXRlbHkgNzAlIG9mIHRoZSAnSERJX2Zvcl95ZWFyJyBjb2x1bW4gY29udGFpbnMgbnVsbCB2YWx1ZXMsIG5lY2Vzc2l0YXRpbmcgYW4gYWRqdXN0bWVudC4gRGVzcGl0ZSBjb21wcmVoZW5zaXZlIGV4cGxvcmF0aW9uLCB3ZSB3ZXJlIHVuYWJsZSB0byBmaW5kIGFueSByZWxpYWJsZSBkYXRhIHRvIGZpbGwgdGhlc2UgbnVsbCB2YWx1ZXMgaW4gdGhlIEhESS4gQWRkaXRpb25hbGx5LCBzaW5jZSB0aGUgZm9ybXVsYSBmb3IgY2FsY3VsYXRpbmcgdGhlIEhESSBjaGFuZ2VkIGluIDIwMTAsIHRoZSBpbmRleCBiZWZvcmUgYW5kIGFmdGVyIHRoaXMgeWVhciBpcyBub3QgZGlyZWN0bHkgY29tcGFyYWJsZS4gQXMgd2UndmUgYWRkZWQgbGlmZSBleHBlY3RhbmN5IGRhdGEgZm9yIGVhY2ggeWVhciwgdGhlIGRlY2lzaW9uIGhhcyBiZWVuIG1hZGUgdG8gZHJvcCB0aGUgJ0hESV9mb3JfeWVhcicgY29sdW1uLgoKYGBge3J9CmRhdGEgPSBzdWJzZXQoZGF0YSwgc2VsZWN0ID0gLWMoSERJX2Zvcl95ZWFyKSApCmBgYApBcHByb3hpbWF0ZWx5IDcuNSUgb2YgdGhlIHRlbXBlcmF0dXJlIGRhdGEgY29uc2lzdHMgb2YgbnVsbCB2YWx1ZXMgd2hpY2ggcmVxdWlyZXMgYWRkcmVzc2luZy4gQXMgYSBmaXJzdCBzdGVwLCB3ZSdsbCBpZGVudGlmeSB0aGUgY291bnRyaWVzIHRoYXQgY29udGFpbiBudWxsIHZhbHVlcyBpbiB0aGVpciB0ZW1wZXJhdHVyZSBkYXRhLgoKYGBge3J9CmRhdGEgJT4lIAogIGdyb3VwX2J5KGNvdW50cnkpICU+JSAKICBmaWx0ZXIoYWxsKGlzLm5hKG1pbl90ZW1wKSkpICU+JSAKICBwdWxsKGNvdW50cnkpICU+JSAKICB1bmlxdWUoKQpgYGAKVGhlcmUncyBvbmx5IG9uZSBjb3VudHJ5LCB0aGUgTWFsZGl2ZXMgKGxvY2F0ZWQgaW4gU291dGggQXNpYSksIGZvciB3aGljaCB0ZW1wZXJhdHVyZSBkYXRhIGlzIHVuYXZhaWxhYmxlLiBHaXZlbiB0aGF0IHdlIGhhdmUgYW1wbGUgZGF0YSBmb3IgQXNpYSwgd2UndmUgbWFkZSB0aGUgZGVjaXNpb24gdG8gZXhjbHVkZSB0aGUgTWFsZGl2ZXMgZnJvbSBvdXIgZGF0YXNldC4KCmBgYHtyfQpkYXRhICU+JQogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lCiAgc3VtbWFyaXNlKG51bV9jb3VudHJpZXMgPSBuX2Rpc3RpbmN0KGNvdW50cnkpKQpgYGAKCmBgYHtyfQpjb3VudHJpZXNfdG9fcmVtb3ZlIDwtIGMoIk1hbGRpdmVzIikKCmRhdGEgPC0gZGF0YVshZGF0YSRjb3VudHJ5ICVpbiUgY291bnRyaWVzX3RvX3JlbW92ZSwgXQpgYGAKCkdpdmVuIHRoYXQgb3VyIHRlbXBlcmF0dXJlIGRhdGEgY29uY2x1ZGVzIGluIDIwMTMsIHdlIHdpbGwgZmlsbCB0aGUgJ2F2Z190ZW1wJywgJ21pbl90ZW1wJywgYW5kICdtYXhfdGVtcCcgZmllbGRzIGZvciB0aGUgeWVhcnMgMjAxNCBhbmQgMjAxNSB1c2luZyB0aGUgY29ycmVzcG9uZGluZyBkYXRhIGZyb20gMjAxMy4gCgpXZSBvYnNlcnZlZCB0aGF0IHRoZSBkYXRhIGZvciBVa3JhaW5lIGluIDIwMTMgaXMgbWlzc2luZyBmcm9tIG91ciBzdWljaWRlIGRhdGFzZXQuIENvbnNlcXVlbnRseSwgdG8gYWRkcmVzcyB0aGlzIGFic2VuY2UsIHdlIHdpbGwgdXNlIHRoZSBkYXRhIGZyb20gMjAxMiBmb3IgdGhpcyBwYXJ0aWN1bGFyIGNvdW50cnkgdG8gYXBwcm94aW1hdGUgdGhlIHZhbHVlcyBmb3IgMjAxNCBhbmQgMjAxNS4KCgpgYGB7cn0KZGZfMjAxMyA8LSBkYXRhICU+JQogIGZpbHRlcih5ZWFyID09IDIwMTMpICU+JQogIHNlbGVjdChjb3VudHJ5LCBhdmdfdGVtcCwgbWluX3RlbXAsIG1heF90ZW1wKQoKbmFtZXMoZGZfMjAxMylbMjo0XSA8LSBwYXN0ZTAobmFtZXMoZGZfMjAxMylbMjo0XSwgIl8yMDEzIikKCmRmXzIwMTIgPC0gZGF0YSAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDEyKSAlPiUKICBzZWxlY3QoY291bnRyeSwgYXZnX3RlbXAsIG1pbl90ZW1wLCBtYXhfdGVtcCkKCm5hbWVzKGRmXzIwMTIpWzI6NF0gPC0gcGFzdGUwKG5hbWVzKGRmXzIwMTIpWzI6NF0sICJfMjAxMiIpCgojIFJlcGxhY2UgTkEgdmFsdWVzIGluIDIwMTQgYW5kIDIwMTUgdXNpbmcgdGhlIGxvb2t1cCB0YWJsZQpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKHllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhcikpICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoCiAgICBhdmdfdGVtcCA9IGlmZWxzZSh5ZWFyICVpbiUgYygiMjAxNCIsICIyMDE1IikgJiBpcy5uYShhdmdfdGVtcCkgJiBjb3VudHJ5ICE9ICJVa3JhaW5lIiwKICAgICAgICAgICAgICAgICAgICAgIGRmXzIwMTMkYXZnX3RlbXBfMjAxM1tkZl8yMDEzJGNvdW50cnkgPT0gY291bnRyeV0sCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoeWVhciAlaW4lIGMoIjIwMTQiLCAiMjAxNSIpICYgaXMubmEoYXZnX3RlbXApICYgY291bnRyeSA9PSAiVWtyYWluZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfMjAxMiRhdmdfdGVtcF8yMDEyW2RmXzIwMTIkY291bnRyeSA9PSBjb3VudHJ5XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfdGVtcCkpLAogICAgbWluX3RlbXAgPSBpZmVsc2UoeWVhciAlaW4lIGMoIjIwMTQiLCAiMjAxNSIpICYgaXMubmEobWluX3RlbXApICYgY291bnRyeSAhPSAiVWtyYWluZSIsCiAgICAgICAgICAgICAgICAgICAgICBkZl8yMDEzJG1pbl90ZW1wXzIwMTNbZGZfMjAxMyRjb3VudHJ5ID09IGNvdW50cnldLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHllYXIgJWluJSBjKCIyMDE0IiwgIjIwMTUiKSAmIGlzLm5hKG1pbl90ZW1wKSAmIGNvdW50cnkgPT0gIlVrcmFpbmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRmXzIwMTIkbWluX3RlbXBfMjAxMltkZl8yMDEyJGNvdW50cnkgPT0gY291bnRyeV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX3RlbXApKSwKICAgIG1heF90ZW1wID0gaWZlbHNlKHllYXIgJWluJSBjKCIyMDE0IiwgIjIwMTUiKSAmIGlzLm5hKG1heF90ZW1wKSAmIGNvdW50cnkgIT0gIlVrcmFpbmUiLAogICAgICAgICAgICAgICAgICAgICAgZGZfMjAxMyRtYXhfdGVtcF8yMDEzW2RmXzIwMTMkY291bnRyeSA9PSBjb3VudHJ5XSwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSh5ZWFyICVpbiUgYygiMjAxNCIsICIyMDE1IikgJiBpcy5uYShtYXhfdGVtcCkgJiBjb3VudHJ5ID09ICJVa3JhaW5lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl8yMDEyJG1heF90ZW1wXzIwMTJbZGZfMjAxMiRjb3VudHJ5ID09IGNvdW50cnldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF90ZW1wKSkKICApICU+JQogIHVuZ3JvdXAoKQpgYGAKCmBgYHtyfQpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKHllYXIgPSBhcy5pbnRlZ2VyKHllYXIpKQpgYGAKCmBgYHtyfQpkaW0oZGF0YSkKYGBgCgpgYGB7cn0KbmFfY291bnRzIDwtIHNhcHBseShkYXRhLCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpL25yb3coZGF0YSkqMTAwKQpuYV9jb3VudHNfZGYgPC0gZGF0YS5mcmFtZShGZWF0dXJlID0gbmFtZXMobmFfY291bnRzKSwgTkFfcmF0aW8gPSBuYV9jb3VudHMpCm5hX2NvdW50c19kZiA9IG5hX2NvdW50c19kZiAlPiUgYHJvd25hbWVzPC1gKCBOVUxMICkKcHJpbnQoZmlsdGVyKG5hX2NvdW50c19kZiwgTkFfcmF0aW8+MCkpCmBgYAoKV2l0aCBhbGwgbWlzc2luZyB2YWx1ZXMgZWZmZWN0aXZlbHkgaGFuZGxlZCwgb3VyIGRhdGFzZXQgaXMgbm93IGNsZWFuIGFuZCByZWFkeSBmb3IgZnVydGhlciBhbmFseXNpcy4gCgoKIyMgMi4zIEZhY3Rvcml6aW5nIENhdGVnb3JpY2FsIERhdGEKCmBgYHtyfQojIE5vbWluYWwgZmFjdG9ycwpkYXRhX25vbWluYWwgPC0gYygnY291bnRyeScsICdzZXgnLCAnY29udGluZW50JykKCmRhdGFbZGF0YV9ub21pbmFsXSA8LSBsYXBwbHkoZGF0YVtkYXRhX25vbWluYWxdLCBmdW5jdGlvbih4KXtmYWN0b3IoeCl9KQoKCiMgTWFraW5nIGFnZSBvcmRpbmFsCmRhdGEkYWdlIDwtIGZhY3RvcihkYXRhJGFnZSwgCiAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgCiAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCI1LTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjE1LTI0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyNS0zNCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMzUtNTQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjU1LTc0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI3NSsiKSkKCiMgTWFraW5nIGdlbmVyYXRpb24gb3JkaW5hbApkYXRhJGdlbmVyYXRpb24gPC0gZmFjdG9yKGRhdGEkZ2VuZXJhdGlvbiwgCiAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgCiAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJHLkkuIEdlbmVyYXRpb24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNpbGVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCb29tZXJzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHZW5lcmF0aW9uIFgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1pbGxlbmlhbHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdlbmVyYXRpb24gWiIpKQoKZGF0YSA8LSBhc190aWJibGUoZGF0YSkKYGBgCgoKIyMgMi40IE91dGxpZXJzCgpEZXRlY3RpbmcgYW5kIGFkZHJlc3Npbmcgb3V0bGllcnMgaXMgYSBmdW5kYW1lbnRhbCBzdGVwIGluIGRhdGEgcHJlcHJvY2Vzc2luZywgZXNwZWNpYWxseSBmb3IgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzIHRoYXQgYXJlIHNpZ25pZmljYW50bHkgaW5mbHVlbmNlZCBieSBvdXRsaWVycy4KClNldmVyYWwgbWV0aG9kcyBleGlzdCB0byBpZGVudGlmeSBvdXRsaWVycywgaW5jbHVkaW5nOgoKKiAqKlZpc3VhbCBJbnNwZWN0aW9uKiogdXNpbmcgQm94cGxvdHMgYW5kIFNjYXR0ZXJwbG90czogVGhlc2UgcGxvdHMgb2ZmZXIgYSBzdHJhaWdodGZvcndhcmQgd2F5IHRvIHZpc3VhbGx5IGlkZW50aWZ5IG91dGxpZXJzLiBCb3hwbG90cyBhcmUgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgdW5pdmFyaWF0ZSBhbmFseXNpcywgd2hpbGUgc2NhdHRlcnBsb3RzIGZhY2lsaXRhdGUgYml2YXJpYXRlIGFuYWx5c2lzLgoKKiAqKlotU2NvcmUgTWV0aG9kKio6IFRoaXMgdGVjaG5pcXVlIGxhYmVscyBhbnkgZGF0YSBwb2ludCB0aGF0IGRldmlhdGVzIG1vcmUgdGhhbiB0aHJlZSBzdGFuZGFyZCBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4gYXMgYW4gb3V0bGllci4gSG93ZXZlciwgdGhpcyBtZXRob2QgaXMgb25seSBlZmZlY3RpdmUgd2hlbiB0aGUgZGF0YSBpcyBjb21wbGV0ZWx5IG9yIG5lYXJseSBub3JtYWxseSBkaXN0cmlidXRlZC4gSGVuY2UsIGl0J3Mgbm90IGlkZWFsIGZvciBza2V3ZWQgZGF0YS4KCiogKipUdWtleSdzIEZlbmNlcyoqOkl0IGlzIGNhbGN1bGF0ZWQgYnkgY3JlYXRpbmcgYSDigJxmZW5jZeKAnSBib3VuZGFyeSBhIGRpc3RhbmNlIG9mIDEuNSBJUVIgYmV5b25kIHRoZSAxc3QgYW5kIDNyZCBxdWFydGlsZXMuIEFueSBkYXRhIGJleW9uZCB0aGVzZSBmZW5jZXMgYXJlIGNvbnNpZGVyZWQgdG8gYmUgb3V0bGllcnMuVGhpcyBtZXRob2QgcHJvdmlkZXMgYSByb2J1c3QgbWVjaGFuaXNtIHRvIHNwb3Qgb3V0bGllcnMsIGV2ZW4gaW4gc2tld2VkIGRpc3RyaWJ1dGlvbnMuCgpPbmNlIG91dGxpZXJzIGFyZSBpZGVudGlmaWVkLCB3ZSBjYW4gZW1wbG95IHNldmVyYWwgc3RyYXRlZ2llcyB0byBoYW5kbGUgdGhlbSwgc3VjaCBhczoKCiogKipSZXNjYWxpbmcgYW5kIFRyYW5zZm9ybWluZyBEYXRhKio6IFRlY2huaXF1ZXMgc3VjaCBhcyBsb2cgdHJhbnNmb3JtYXRpb24sIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uLCBvciBjdWJlIHJvb3QgdHJhbnNmb3JtYXRpb24gY2FuIGhlbHAgbGVzc2VuIHRoZSBkYXRhIHNrZXduZXNzIGFuZCBtaXRpZ2F0ZSB0aGUgZWZmZWN0cyBvZiBvdXRsaWVycy4KCiogKipUcnVuY2F0aW9uIG9yIFdpbnNvcml6YXRpb24qKjogVGhpcyBtZXRob2QgY2FwcyB0aGUgb3V0bGllcnMgYXQgYSBzcGVjaWZpZWQgcGVyY2VudGlsZSBvZiB0aGUgZGF0YSwgbGlrZSB0aGUgNXRoIG9yIDk1dGggcGVyY2VudGlsZS4KCiogKipSZW1vdmluZyBPdXRsaWVycyoqOiBJbiBleHRyZW1lIGNhc2VzLCB3aGVuIHdlIGFyZSBjb25maWRlbnQgdGhhdCBhbiBvdXRsaWVyIGFyaXNlcyBmcm9tIGluY29ycmVjdCBkYXRhIGVudHJ5IG9yIG1lYXN1cmVtZW50LCB3ZSBtaWdodCBkZWNpZGUgdG8gZWxpbWluYXRlIHRoZXNlIHZhbHVlcyB0byBhdm9pZCB0aGVpciB1bmR1ZSBpbmZsdWVuY2Ugb24gb3VyIG1vZGVsLiBIb3dldmVyLCB0aGlzIG1ldGhvZCBzaG91bGQgYmUgYSBsYXN0IHJlc29ydCwgYXMgaXQgbWlnaHQgcmVzdWx0IGluIGluZm9ybWF0aW9uIGxvc3MgYW5kIHNob3VsZCBiZSBqdXN0aWZpZWQgdGhvcm91Z2hseS4KCgpMZXQncyBiZWdpbiBieSBleGFtaW5pbmcgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgcmFuZ2Ugb2Ygb3VyIGZlYXR1cmVzIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBzcHJlYWQgYW5kIGRpc3BlcnNpb24gb2Ygb3VyIGRhdGEuCgpgYGB7cn0Kc3VtbWFyeShkYXRhKQpgYGAKCiMjIyAyLjQuMSBWaXN1YWxpemUgRGlzdHJpYnV0aW9uIG9mIHRoZSBEYXRhCgpgYGB7cn0KCiMgU2V0IHRoZSBvdmVyYWxsIGxheW91dCBmb3IgdGhlIGNvbWJpbmVkIHBsb3QKcGFyKG1mcm93ID0gYygzLCA0KSkKcGFyKG1hciA9IGMoMiwgMiwgMiwgMikpICAjIEFkanVzdCB0aGUgbWFyZ2lucyBmb3IgZWFjaCBwbG90CgojIGZvciBlYWNoIGNvbHVtbiBpbiB0aGUgZGF0YWZyYW1lCgpmb3IoY29sIGluIG5hbWVzKGRhdGEpKSB7CiAgIyBpZiBpdCdzIGEgbnVtZXJpYyBjb2x1bW4KICBpZihpcy5udW1lcmljKGRhdGFbW2NvbF1dKSkgewogICAgIyBjcmVhdGUgYSBoaXN0b2dyYW0KICAgIGhpc3QoZGF0YVtbY29sXV0sIG1haW49Y29sLCB4bGFiPWNvbCwgY29sID0gIiMxMzUyN2EiLCBib3JkZXIgPSAiI2ViZWJlYiIsIGNleC5tYWluID0gMSkKICB9Cn0KCmBgYAoKIyMjIDIuNC4yIEJveFBsb3RzCgpgYGB7cn0KIyBTZXQgdGhlIG92ZXJhbGwgbGF5b3V0IGZvciB0aGUgY29tYmluZWQgcGxvdApwYXIobWZyb3cgPSBjKDIsIDQpKQpwYXIobWFyID0gYygyLCAyLCAyLCAyKSkgICMgQWRqdXN0IHRoZSBtYXJnaW5zIGZvciBlYWNoIHBsb3QKCiMgZm9yIGVhY2ggY29sdW1uIGluIHRoZSBkYXRhZnJhbWUKCmZvcihjb2wgaW4gbmFtZXMoZGF0YSkpIHsKICAjIGlmIGl0J3MgYSBudW1lcmljIGNvbHVtbgogIGlmKGlzLm51bWVyaWMoZGF0YVtbY29sXV0pKSB7CiAgICAjIGNyZWF0ZSBhIGhpc3RvZ3JhbQogICAgYm94cGxvdChkYXRhW1tjb2xdXSwgbWFpbj1jb2wsIHhsYWI9Y29sLCBjb2wgPSAiI2ViZWJlYiIsIGJvcmRlciA9ICIjMTM1MjdhIiwgY2V4Lm1haW4gPSAxKQogIH0KfQoKYGBgCkZyb20gdGhlIGFib3ZlIGJveHBsb3RzLCBpdCdzIGV2aWRlbnQgdGhhdCAic3VpY2lkZXNfbm8iLCAicG9wdWxhdGlvbiIsIGFuZCAiR0RQX2Zvcl95ZWFyIiBhbGwgZXhoaWJpdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBvdXRsaWVycy4gQWRkaXRpb25hbGx5LCAic3VpY2lkZV9yYXRpbyIgYW5kICJHRFBfcGVyX2NhcGl0YSIgYWxzbyBzaG93IGEgc3Vic3RhbnRpYWwgbnVtYmVyIG9mIG91dGxpZXIgdmFsdWVzLgoKCk91ciBleGFtaW5hdGlvbiBvZiB0aGUgYm94cGxvdHMgYW5kIGhpc3RvZ3JhbXMgcmV2ZWFscyBhIHNpZ25pZmljYW50IGNvbmNlbnRyYXRpb24gb2YgZGF0YSB3aXRoaW4gdGhlICdzdWljaWRlX25vJyBhbmQgJ3N1aWNpZGVfcmF0aW8nIHBhcmFtZXRlcnMsIHNrZXdlZCB0b3dhcmRzIHplcm8uIFRoaXMgaGlnaCBkZW5zaXR5IGFyb3VuZCB6ZXJvIG1hbmlmZXN0cyBhcyBhIGxvbmcgdGFpbCBpbiB0aGUgZGlzdHJpYnV0aW9uIHRvd2FyZHMgdGhlIHJpZ2h0LgpJbiBvcmRlciB0byBhZGRyZXNzIHRoaXMgc2tld25lc3MsIHdlIG5lZWQgdG8gZGVsdmUgZGVlcGVyIGludG8gdGhlIHJlY29yZHMgd2hlcmUgJ3N1aWNpZGVfbm8nIGlzIHJlY29yZGVkIGFzIHplcm8uCgoKYGBge3J9CiMgRGVmaW5lIGEgY29tbW9uIHRoZW1lCmNvbW1vbl90aGVtZSA8LSB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleSIsIGxpbmV3aWR0aCA9IDAuMSksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgKQoKIyBEZWZpbmUgYSBjb21tb24gY29sb3IKY29tbW9uX2NvbG9yIDwtICJzdGVlbGJsdWUiCgojIERhdGEgZm9yIHRoZSBmaXJzdCBwbG90Cnplcm9fc3VpY2lkZXNfZGF0YSA8LSBkYXRhICU+JQogIGZpbHRlcihzdWljaWRlc19ubyA9PSAwKSAlPiUKICBncm91cF9ieShhZ2UpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYWdlLCB5ID0gY291bnQsIGZpbGw9YWdlKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICAgIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIkNvdW50IiwgCiAgICAgICAgIHRpdGxlID0gIlplcm8gU3VpY2lkZXMgYnkgQWdlIEdyb3VwIikgKwogICAgY29tbW9uX3RoZW1lCgojIERhdGEgZm9yIHRoZSBzZWNvbmQgcGxvdAphZ2VfcGxvdCA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGFnZSkgJT4lCiAgc3VtbWFyaXplKHN1aWNpZGVfcGVyXzEwMGsgPSAoc3VtKGFzLm51bWVyaWMoc3VpY2lkZXNfbm8pKSAvIHN1bShhcy5udW1lcmljKHBvcHVsYXRpb24pKSkgKiAxMDAwMDApICU+JQogIGdncGxvdChhZXMoeCA9IGFnZSwgeSA9IHN1aWNpZGVfcGVyXzEwMGssIGZpbGwgPSBhZ2UpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIGxhYnModGl0bGUgPSAiR2xvYmFsIHN1aWNpZGVzIHBlciAxMDBrLCBieSBBZ2UiLAogICAgICAgeCA9ICJBZ2UiLCAKICAgICAgIHkgPSAiU3VpY2lkZXMgcGVyIDEwMGsiKSArCiAgY29tbW9uX3RoZW1lICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDMwLCAxKSwgbWlub3JfYnJlYWtzID0gTlVMTCkgIyBDaGFuZ2VkIGJyZWFrcyBmb3IgYmV0dGVyIHZpc2liaWxpdHkKCiMgQXJyYW5nZSB0aGUgcGxvdHMKZ3JpZC5hcnJhbmdlKGFnZV9wbG90LCB6ZXJvX3N1aWNpZGVzX2RhdGEsIG5jb2wgPSAyKQoKYGBgCk91ciBvYnNlcnZhdGlvbnMgc3VnZ2VzdCB0aGF0IGEgc2lnbmlmaWNhbnQgcHJvcG9ydGlvbiBvZiB6ZXJvLXN1aWNpZGUgaW5zdGFuY2VzIGFyZSB3aXRoaW4gdGhlIDUtMTQgYWdlIGJyYWNrZXQuIFRoZSBvdmVyYWxsIHN1aWNpZGUgYXZlcmFnZSBmb3IgdGhpcyBhZ2UgZ3JvdXAgaXMgbm90YWJseSBsb3csIGdlbmVyYWxseSB1bmRlciBvbmUuCgpUaGlzIGFnZSBncm91cCwgaG93ZXZlciwgZG9lc24ndCBhZGVxdWF0ZWx5IHJlcHJlc2VudCB0aGUgbGFyZ2VyIHBvcHVsYXRpb24uIFRoZSByZWFzb25zIGZvciBzdWljaWRlIHdpdGhpbiB0aGlzIGFnZSBicmFja2V0IGFyZSBsaWtlbHkgdG8gYmUgZnVuZGFtZW50YWxseSBkaWZmZXJlbnQgZnJvbSB0aG9zZSBvZiBvdGhlciBncm91cHMsIHBvdGVudGlhbGx5IGluZmx1ZW5jZWQgYnkgdW5pcXVlIGNhdXNlcy4KCkdpdmVuIHRoZXNlIGNvbnNpZGVyYXRpb25zLCB3ZSBoYXZlIG9wdGVkIHRvIGV4Y2x1ZGUgdGhlIDUtMTQgYWdlIGdyb3VwIGZyb20gb3VyIGRhdGEuIFRoaXMgZGVjaXNpb24gc3RlbXMgZnJvbSB0aGUgcmVhbGl6YXRpb24gdGhhdCB0aGlzIGdyb3VwIGV4aGliaXRzIGRpc3RpbmN0bHkgZGlmZmVyZW50IGJlaGF2aW9ycyBhbmQgaXMgbm90IHJlcHJlc2VudGF0aXZlIG9mIHRoZSBicm9hZGVyIHBvcHVsYXRpb24gaW4gdGhlIGNvbnRleHQgb2Ygc3VpY2lkZSByYXRlcy4KCmBgYHtyfQpkYXRhIDwtIGRhdGElPiUKICBmaWx0ZXIoYWdlICE9ICc1LTE0JykKYGBgCgpgYGB7cn0KZGltKGRhdGEpCmBgYAoKIyMjIDIuNC4zIFR1a2V5J3MgRmVuY2VzCgpBcyBpbmRpY2F0ZWQgYnkgdGhlIGhpc3RvZ3JhbXMgYWJvdmUsIG1vc3Qgb2Ygb3VyIGRhdGEgZG9lcyBub3QgYWRoZXJlIHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVG8geWllbGQgbW9yZSBzcGVjaWZpYyByZXN1bHRzLCB3ZSB3aWxsIGluaXRpYWxseSBlbXBsb3kgVHVrZXkncyBGZW5jZXMgbWV0aG9kIHRvIGlkZW50aWZ5IHRoZSBudW1iZXIgb2Ygb3V0bGllcnMgaW4gZWFjaCBmZWF0dXJlLgoKYGBge3J9CiNjaGVjayB0aGUgbnVtYmVyIG9mIG91dGxpZXJzIGluIGVhY2ggZmVhdHVyZXMKCiMgRGVmaW5lIHRoZSBvdXRsaWVyX2NvdW50IGZ1bmN0aW9uClR1a2V5X291dGxpZXJfY291bnQgPC0gZnVuY3Rpb24oY29sKSB7CiAgcTc1IDwtIHF1YW50aWxlKGNvbCwgMC43NSwgbmEucm09IFRSVUUpCiAgcTI1IDwtIHF1YW50aWxlKGNvbCwgMC4yNSwgbmEucm09IFRSVUUpCiAgaXFyIDwtIHE3NSAtIHEyNQogIG1pbl92YWwgPC0gcTI1IC0gKGlxciAqIDEuNSkKICBtYXhfdmFsIDwtIHE3NSArIChpcXIgKiAxLjUpCiAgb3V0bGllcl9jb3VudCA8LSBzdW0oY29sID4gbWF4X3ZhbCB8IGNvbCA8IG1pbl92YWwpCiAgb3V0bGllcl9wZXJjZW50IDwtIHJvdW5kKG91dGxpZXJfY291bnQgLyBsZW5ndGgoY29sKSAqIDEwMCwgMikKICByZXR1cm4oYyhvdXRsaWVyX2NvdW50LCBvdXRsaWVyX3BlcmNlbnQpKQp9CgojIEdldCBudW1lcmljIGRhdGEKbnVtZXJpY19kYXRhIDwtIGRhdGFbLCBzYXBwbHkoZGF0YSwgaXMubnVtZXJpYyldCgojIEFwcGx5IHRoZSBmdW5jdGlvbiB0byBudW1lcmljIGNvbHVtbnMKb3V0bGllcnMgPC0gc2FwcGx5KG51bWVyaWNfZGF0YSwgVHVrZXlfb3V0bGllcl9jb3VudCkKCiMgQ29udmVydCB0byBkYXRhZnJhbWUKb3V0bGllcnNfZGYgPC0gYXMuZGF0YS5mcmFtZSh0KG91dGxpZXJzKSkKY29sbmFtZXMob3V0bGllcnNfZGYpIDwtIGMoIk91dGxpZXJfQ291bnQiLCAiT3V0bGllcl9QZXJjZW50IikKCiMgUHJpbnQgdGhlIHJlc3VsdApwcmludChvdXRsaWVyc19kZikKYGBgCgojIyMgMi40LjQgVHJhbnNmb3JtYXRpb25zIAoKVGhlIHJlc3VsdHMgZnJvbSB0aGUgVHVrZXkncyBtZXRob2QsIGNvbnNpc3RlbnQgd2l0aCBvdXIgYm94cGxvdCBvYnNlcnZhdGlvbnMsIHJldmVhbCBhIHNpZ25pZmljYW50IHByb3BvcnRpb24gb2Ygb3V0bGllcnMgaW4gdGhlICJzdWljaWRlc19ubyIsICJzdWljaWRlX3JhdGlvIiwgInBvcHVsYXRpb24iLCBhbmQgIkdEUF9mb3JfeWVhciIgZGF0YS4gSW4gYW4gYXR0ZW1wdCB0byByZWR1Y2UgdGhlIGltcGFjdCBvZiB0aGVzZSBvdXRsaWVycywgd2UgcGxhbiB0byBlbnJpY2ggb3VyIGRhdGFzZXQgd2l0aCBhZGRpdGlvbmFsIGNvbHVtbnMsIGVhY2ggcmVwcmVzZW50aW5nIGxvZy10cmFuc2Zvcm1lZCBhbmQgc3F1YXJlIHJvb3QtdHJhbnNmb3JtZWQgdmFsdWVzIG9mIHRoZXNlIHZhcmlhYmxlcy4KCkhvd2V2ZXIsIHdlIGZhY2UgYSBjaGFsbGVuZ2Ugd2l0aCB0aGUgInN1aWNpZGVfbm8iIGFuZCAic3VpY2lkZV9yYXRpbyIgdmFyaWFibGVzIGFzIHRoZXkgY29udGFpbiB6ZXJvIHZhbHVlcywgbWFraW5nIGl0IGltcG9zc2libGUgdG8gYXBwbHkgYSBzdHJhaWdodGZvcndhcmQgbG9nIHRyYW5zZm9ybWF0aW9uLiBUbyBjaXJjdW12ZW50IHRoaXMgaXNzdWUsIHdlJ2xsIGludHJvZHVjZSBhbiBhZGp1c3RtZW50IGZhY3RvciwgYSBjb25zdGFudCBjPTEsIHRvIGFsbCBzdWljaWRlIG51bWJlcnMuIFN1YnNlcXVlbnRseSwgd2UnbGwgY29tcHV0ZSBhIG5ldyByYXRpbyBhbmQgYXBwbHkgYSBsb2cgdHJhbnNmb3JtYXRpb24gdG8gaXQuIFRoaXMgYXBwcm9hY2ggZW5zdXJlcyBhIHNtb290aCBhbmQgc3VjY2Vzc2Z1bCB0cmFuc2Zvcm1hdGlvbiBwcm9jZXNzLgoKYGBge3J9CiMgQWRkIGEgc21hbGwgY29uc3RhbnQgdG8gYXZvaWQgdW5kZWZpbmVkIGxvZyB2YWx1ZXMKYyA8LSAxCgpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKG5ld19zdWljaWRlc19ubyA9IHN1aWNpZGVzX25vICsgYywKICAgICAgICAgbmV3X3N1aWNpZGVfcmF0aW8gPSBuZXdfc3VpY2lkZXNfbm8gLyBwb3B1bGF0aW9uLAogICAgICAgICBsb2dfcG9wdWxhdGlvbiA9IGxvZyhwb3B1bGF0aW9uKSwKICAgICAgICAgbG9nX0dEUF95ZWFyID0gbG9nKEdEUF9mb3JfeWVhciksCiAgICAgICAgIGxvZ19HRFBfY2FwaXRhID0gbG9nKEdEUF9wZXJfY2FwaXRhKSwKICAgICAgICAgbG9nX3N1aWNpZGVfbm8gPSBsb2cobmV3X3N1aWNpZGVzX25vKSwKICAgICAgICAgbG9nX3N1aWNpZGVfcmF0aW8gPSBsb2cobmV3X3N1aWNpZGVfcmF0aW8pCiAgICAgICAgICkKYGBgCgpJbiBvdXIgYW5hbHlzaXMsIHdlIG9wdGVkIGZvciB0aGUgbmF0dXJhbCBsb2dhcml0aG0gZm9yIGl0cyBlYXNlIG9mIGludGVycHJldGF0aW9uLiBXaGlsZSBsb2dhcml0aG1pYyB0cmFuc2Zvcm1hdGlvbnMgd2l0aCBkaWZmZXJlbnQgYmFzZXMgZG9uJ3QgYWx0ZXIgdGhlIGRpc3RyaWJ1dGlvbidzIGZvcm0sIHRoZXkgZG8gaGF2ZSBpbXBsaWNhdGlvbnMgZm9yIGhvdyB3ZSBpbnRlcnByZXQgdGhlIGNvZWZmaWNpZW50cyBpbiBvdXIgbW9kZWwuCgpXaXRoIHRoZSBuYXR1cmFsIGxvZ2FyaXRobSAoYmFzZSBlKSwgY29lZmZpY2llbnRzIGluIGEgbW9kZWwgd2hlcmUgYm90aCB0aGUgcHJlZGljdG9yICh4KSBhbmQgcmVzcG9uc2UgKHkpIHZhcmlhYmxlcyBhcmUgbG9nLXRyYW5zZm9ybWVkIGluZGljYXRlIHRoZSBwZXJjZW50YWdlIGNoYW5nZSBpbiB5IGNvcnJlc3BvbmRpbmcgdG8gYSAxJSBjaGFuZ2UgaW4geC4KCk9uIHRoZSBvdGhlciBoYW5kLCBpZiBhIGJhc2UtMTAgbG9nYXJpdGhtIHdlcmUgdXNlZCBpbiB0aGUgc2FtZSBjaXJjdW1zdGFuY2VzLCBlYWNoIGNvZWZmaWNpZW50IHdvdWxkIHJlcHJlc2VudCB0aGUgY2hhbmdlIGluIHkgYXNzb2NpYXRlZCB3aXRoIGEgMTAlIGNoYW5nZSBpbiB4LgoKVGhlcmVmb3JlLCBieSB1c2luZyB0aGUgbmF0dXJhbCBsb2dhcml0aG0sIHdlIHNpbXBsaWZ5IHRoZSBpbnRlcnByZXRhdGlvbiBvZiBvdXIgbW9kZWwncyBvdXRwdXQsIGVuYWJsaW5nIG1vcmUgc3RyYWlnaHRmb3J3YXJkIGNvbmNsdXNpb25zIGFuZCBkaXNjdXNzaW9ucy4KCmBgYHtyfQpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKHNxcnRfcG9wdWxhdGlvbiA9IHNxcnQocG9wdWxhdGlvbiksCiAgICAgICAgIHNxcnRfR0RQX3llYXIgPSBzcXJ0KEdEUF9mb3JfeWVhciksCiAgICAgICAgIHNxcnRfR0RQX2NhcGl0YSA9IHNxcnQoR0RQX3Blcl9jYXBpdGEpLAogICAgICAgICBzcXJ0X3N1aWNpZGVfbm8gPSBzcXJ0KHN1aWNpZGVzX25vKSwKICAgICAgICAgc3FydF9zdWljaWRlX3JhdGlvID0gc3FydChzdWljaWRlX3JhdGlvKQogICAgICAgICApCmBgYAoKCmBgYHtyfQojIERlZmluZSB0aGUgbGlzdCBvZiBjb2x1bW5zIHRvIGJlIHByb2Nlc3NlZAp0cmFuc2Zvcm1lZF9jb2wgPSBjKCJwb3B1bGF0aW9uIiwibG9nX3BvcHVsYXRpb24iLCAic3FydF9wb3B1bGF0aW9uIiwKICAgICAgICAgICAgICAgICAgICAiR0RQX2Zvcl95ZWFyIiwgImxvZ19HRFBfeWVhciIsICJzcXJ0X0dEUF95ZWFyIiwgCiAgICAgICAgICAgICAgICAgICAgIkdEUF9wZXJfY2FwaXRhIiwgImxvZ19HRFBfY2FwaXRhIiwgInNxcnRfR0RQX2NhcGl0YSIgLCAKICAgICAgICAgICAgICAgICAgICAic3VpY2lkZXNfbm8iLCAibG9nX3N1aWNpZGVfbm8iLCAic3FydF9zdWljaWRlX25vIiwKICAgICAgICAgICAgICAgICAgICAic3VpY2lkZV9yYXRpbyIsImxvZ19zdWljaWRlX3JhdGlvIiwgInNxcnRfc3VpY2lkZV9yYXRpbyIpCgojIEFwcGx5IHRoZSBmdW5jdGlvbiB0byBudW1lcmljIGNvbHVtbnMKdHJhbnNmb3JtZWRfZGF0YSA8LSBkYXRhWywgdHJhbnNmb3JtZWRfY29sXQp0cmFuc2Zvcm1lZF9vdXRsaWVycyA8LSBzYXBwbHkodHJhbnNmb3JtZWRfZGF0YSwgVHVrZXlfb3V0bGllcl9jb3VudCkKCiMgQ29udmVydCB0byBkYXRhZnJhbWUKb3V0bGllcnNfZGYgPC0gYXMuZGF0YS5mcmFtZSh0KHRyYW5zZm9ybWVkX291dGxpZXJzKSkKY29sbmFtZXMob3V0bGllcnNfZGYpIDwtIGMoIk91dGxpZXJfQ291bnQiLCAiT3V0bGllcl9QZXJjZW50IikKCiMgUHJpbnQgdGhlIHJlc3VsdApwcmludChvdXRsaWVyc19kZikKYGBgCgoKYGBge3J9CgojIFNldCB0aGUgb3ZlcmFsbCBsYXlvdXQgZm9yIHRoZSBjb21iaW5lZCBwbG90CnBhcihtZnJvdyA9IGMoMywgNCkpCnBhcihtYXIgPSBjKDIsIDIsIDIsIDIpKSAgIyBBZGp1c3QgdGhlIG1hcmdpbnMgZm9yIGVhY2ggcGxvdAoKIyBmb3IgZWFjaCBjb2x1bW4gaW4gdGhlIGRhdGFmcmFtZQoKZm9yKGNvbCBpbiBuYW1lcyhkYXRhKSkgewogICMgaWYgaXQncyBhIG51bWVyaWMgY29sdW1uCiAgaWYoaXMubnVtZXJpYyhkYXRhW1tjb2xdXSkpIHsKICAgICMgY3JlYXRlIGEgaGlzdG9ncmFtCiAgICBoaXN0KGRhdGFbW2NvbF1dLCBtYWluPWNvbCwgeGxhYj1jb2wsIGNvbCA9ICIjMTM1MjdhIiwgYm9yZGVyID0gIiNlYmViZWIiLCBjZXgubWFpbiA9IDEpCiAgfQp9CmBgYAoKVXBvbiBhcHBseWluZyB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uLCB3ZSBub3RpY2VkIGEgcmVtYXJrYWJsZSBkZWNyZWFzZSBpbiB0aGUgbnVtYmVyIG9mIG91dGxpZXJzLiBNb3Jlb3ZlciwgdGhlIHRyYW5zZm9ybWVkIGRhdGEgc2hvd2VkIGEgdGVuZGVuY3kgdG93YXJkcyBhIG1vcmUgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgaW5kaWNhdGluZyB0aGUgZWZmZWN0aXZlbmVzcyBvZiB0aGUgdHJhbnNmb3JtYXRpb24uCgpXaXRoIHRoZSBkYXRhIGRpc3RyaWJ1dGlvbiBub3cgbGVzcyBza2V3ZWQgYW5kIG1vcmUgYWtpbiB0byBhIG5vcm1hbCBkaXN0cmlidXRpb24sIHdlIGxldmVyYWdlZCB0aGUgWi1zY29yZSBtZXRob2QgdG8gZnVydGhlciBxdWFudGlmeSB0aGUgcmVtYWluaW5nIG91dGxpZXJzIGluIGVhY2ggY29sdW1uLiBUaGlzIGFsbG93ZWQgdXMgYSBtb3JlIHByZWNpc2UgZXhhbWluYXRpb24gb2YgdGhlIGRhdGEgc3ByZWFkIGFuZCBvdXRsaWVyIHByZXZhbGVuY2UuCgpgYGB7cn0KI2RhdGEgPSBzdWJzZXQoZGF0YSwgc2VsZWN0ID0gLWMoc3FydF9wb3B1bGF0aW9uLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcXJ0X0dEUF95ZWFyLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcXJ0X0dEUF9jYXBpdGEsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNxcnRfc3VpY2lkZV9ubywKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3FydF9zdWljaWRlX3JhdGlvKSkKYGBgCgoKCiMjIyAyLjQuNSBaX1Njb3JlCgpOZXh0LCB3ZSBlbXBsb3kgdGhlIFotU2NvcmUgbWV0aG9kIHRvIGlkZW50aWZ5IHBvdGVudGlhbCBvdXRsaWVycyB3aXRoaW4gZWFjaCBmZWF0dXJlLiBUbyBkbyB0aGlzLCB3ZSdsbCBlc3RhYmxpc2ggdXBwZXIgYW5kIGxvd2VyIGJvdW5kcywgYmV5b25kIHdoaWNoIGEgZGF0YSBwb2ludCB3aWxsIGJlIGNsYXNzaWZpZWQgYXMgYW4gb3V0bGllci4gVGhlIGNhbGN1bGF0aW9ucyBmb3IgdGhlc2UgYm91bmRhcmllcyBhcmUgYXMgZm9sbG93czoKClVwcGVyIGxpbWl0OiBNZWFuICsgKDMgKiBTdGFuZGFyZCBEZXZpYXRpb24pCkxvd2VyIGxpbWl0OiBNZWFuIC0gKDMgKiBTdGFuZGFyZCBEZXZpYXRpb24pCgpUaGlzIG1ldGhvZCBpcyBiYXNlZCBvbiB0aGUgcHJpbmNpcGxlIHRoYXQgZm9yIGEgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgZGF0YXNldCwgYWJvdXQgOTkuNyUgb2YgZGF0YSBmYWxscyB3aXRoaW4gdGhyZWUgc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSBtZWFuLiBIZW5jZSwgYW55IGRhdGEgcG9pbnQgYmV5b25kIHRoaXMgcmFuZ2UgY2FuIGJlIGNvbnNpZGVyZWQgYW4gb3V0bGllci4KCmBgYHtyfQpaX1Njb3JlIDwtIGZ1bmN0aW9uKGNvbCl7CiAgcmV0dXJuKChjb2wgLSBtZWFuKGNvbCkpIC8gc2QoY29sKSkKfQoKWl9vdXRsaWVyX2NvdW50IDwtIGZ1bmN0aW9uKGNvbCkgewogIFVwcGVyX2xpbWl0ID0gbWVhbihjb2wpICsgKDMgKiBzZChjb2wpKQogIExvd2VyX2xpbWl0ID0gbWVhbihjb2wpIC0gKDMgKiBzZChjb2wpKQogIG91dGxpZXJfY291bnQgPC0gc3VtKGNvbCA+IFVwcGVyX2xpbWl0fCBjb2wgPCBMb3dlcl9saW1pdCkKICBvdXRsaWVyX3BlcmNlbnQgPC0gcm91bmQob3V0bGllcl9jb3VudCAvIGxlbmd0aChjb2wpICogMTAwLCAyKQogIHJldHVybihjKG91dGxpZXJfY291bnQsIG91dGxpZXJfcGVyY2VudCkpCn0KCiMgR2V0IG51bWVyaWMgZGF0YQpudW1lcmljX2RhdGEgPC0gZGF0YVssIHNhcHBseShkYXRhLCBpcy5udW1lcmljKV0KCiMgQXBwbHkgdGhlIGZ1bmN0aW9uIHRvIG51bWVyaWMgY29sdW1ucwpvdXRsaWVycyA8LSBzYXBwbHkobnVtZXJpY19kYXRhLCBaX291dGxpZXJfY291bnQpCgojIENvbnZlcnQgdG8gZGF0YWZyYW1lCm91dGxpZXJzX2RmIDwtIGFzLmRhdGEuZnJhbWUodChvdXRsaWVycykpCmNvbG5hbWVzKG91dGxpZXJzX2RmKSA8LSBjKCJPdXRsaWVyX0NvdW50IiwgIk91dGxpZXJfUGVyY2VudCIpCgojIFByaW50IHRoZSByZXN1bHQKcHJpbnQob3V0bGllcnNfZGYpCmBgYAogCgoKCiMjIyAyLjQuNiBFeHBsb3JpbmcgT3V0bGllcnMKCkluIG91ciBwcm9qZWN0LCAic3VpY2lkZV9yYXRpbyIgaXMgdGhlIGtleSB2YXJpYWJsZSB3ZSBhaW0gdG8gcHJlZGljdCB1c2luZyBsaW5lYXIgcmVncmVzc2lvbi4gSXQgaXMgaW1wb3J0YW50IHRvIGFja25vd2xlZGdlIHRoYXQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzIGFyZSBwYXJ0aWN1bGFybHkgc3VzY2VwdGlibGUgdG8gdGhlIGluZmx1ZW5jZSBvZiBvdXRsaWVycy4gVGhlcmVmb3JlLCBpdCBpcyBlc3NlbnRpYWwgdG8gYWRlcXVhdGVseSBhZGRyZXNzIGFuZCBtYW5hZ2UgYW55IG91dGxpZXJzIHByZXNlbnQgaW4gdGhlICJzdWljaWRlX3JhdGlvIiB2YXJpYWJsZSwgdG8gZW5zdXJlIG91ciBtb2RlbCdzIGFjY3VyYWN5IGFuZCByZWxpYWJpbGl0eS4gCgpsZXQncyBleHBsb3JlIG91dGxpZXJzIGluIHN1aWNpZGUgcmF0aW8gYW5kIGNoZWNrIGlmIHRoZXJlIGlzIGFueSBwYXR0ZXJuIGluIHRoZW0uIHdlIHVzZSB0dWtleSdzIGZlbmNlIGR0ZWN0ZWQgb3V0bGllcnMuKHRvIGJlIGVkaXRlZCkKCmBgYHtyfQojIENhbGN1bGF0ZSBJUVIgYW5kIGZlbmNlcwpRMSA8LSBxdWFudGlsZShkYXRhJHN1aWNpZGVfcmF0aW8sIDAuMjUpClEzIDwtIHF1YW50aWxlKGRhdGEkc3VpY2lkZV9yYXRpbywgMC43NSkKSVFSIDwtIFEzIC0gUTEKCmxvd2VyX2ZlbmNlIDwtIFExIC0gMS41ICogSVFSCnVwcGVyX2ZlbmNlIDwtIFEzICsgMS41ICogSVFSCgojIEZpbHRlciBvdXRsaWVycwpzdWljaWRlX291dGxpZXJzIDwtIGRhdGEgJT4lIAogIGZpbHRlcihzdWljaWRlX3JhdGlvIDwgbG93ZXJfZmVuY2UgfCBzdWljaWRlX3JhdGlvID4gdXBwZXJfZmVuY2UpCgpgYGAKCmBgYHtyfQpzdW1tYXJ5KGRhdGEpCmBgYAoKYGBge3J9CnN1bW1hcnkoc3VpY2lkZV9vdXRsaWVycykKYGBgClRoZSBhbmFseXNpcyBzaG93cyB0aGF0IHRoZSB0b3Agc2l4IGNvdW50cmllcyB3aXRoIG91dGxpZXIgc3VpY2lkZSByYXRpb3MgYXJlIHRoZSBSdXNzaWFuIEZlZGVyYXRpb24sIEthemFraHN0YW4sIFVrcmFpbmUsIExpdGh1YW5pYSwgSHVuZ2FyeSwgYW5kIEJlbGFydXMuIEludHJpZ3VpbmdseSwgdGhlc2UgbmF0aW9ucyBhcmUgbm90IG9ubHkgZ2VvZ3JhcGhpY2FsbHkgcHJveGltYXRlLCBidXQgYWxzbyBzaGFyZSBjdWx0dXJhbCBhbmQgaGlzdG9yaWNhbCBsaW5rcy4gVGhpcyBvYnNlcnZhdGlvbiBtYXkgaW1wbHkgcG90ZW50aWFsIHJlZ2lvbmFsIHRyZW5kcyBvciBzaGFyZWQgc29jaW8tZWNvbm9taWMgZmFjdG9ycyBpbmZsdWVuY2luZyB0aGUgZWxldmF0ZWQgc3VpY2lkZSByYXRpb3MuIAoKRnVydGhlcm1vcmUsIGEgc3RyaWtpbmcgZGV0YWlsIGVtZXJnZXMgZnJvbSB0aGUgb3V0bGllcnM6IG5lYXJseSBhbGwsIG9yIDk2LjclLCBhcmUgbWVuLiBUaGlzIGZpbmRpbmcgaW5kaWNhdGVzIGEgc2lnbmlmaWNhbnRseSBoaWdoZXIgaW5jaWRlbmNlIG9mIGV4dHJlbWUgc3VpY2lkZSByYXRpb3MgYW1vbmcgbWVuLgoKYGBge3J9CmRpbShzdWljaWRlX291dGxpZXJzKQpgYGAKCgpEdWUgdG8gdGhlIG5hdHVyZSBvZiBvdXIgZGF0YSwgYW5kIHRoZSBhbmFseXNpcyB3ZSBwZXJmb3JtZWQgd2UgYmVsaWV2ZSB0aGF0IHJlbW92aW5nIG91dGxpZXJzIG9yIGFwcGx5aW5nIHRydW5pY2F0aW9uKFdpbnNvcml6YXRpb24pIHdpbGwgY2F1c2UgaW5mb3JtYXRpb24gbG9zcy4gc28gd2Uga2VlcCB0aGUgb3V0bGllcnMgZm9yIEVEQS5hbmQgd2Ugd2lsbCB0cnkgZGlmZmVyZW50IG1ldGhvZHMgb24gbW9kZWxpbmcgdGhlIGRhdGEgdG8gc2VlIHdoaWNoIHBlcmZvcm0gdGhlIGJlc3Qgb24gb3VyIGRhdGEuIHRoZXNlIGFyZSB0aGUgbWV0aG9kcyB3ZSB3aWxsIHRyeToKMS4gcmVtb3ZpbmcgZGV0ZWN0ZWQgb3V0bGllcnMgd2l0aCBib3RoIFR1a2V5J3MgRmVuY2UgYW5kIFpfc2NvcmUgbWV0aG9kLiBXaXRoIHJlbW92aW5nIG91dGxpZXJzIApIb3dldmVyLCB0aGlzIGNhbiBiZSByaXNreSBiZWNhdXNlIGl0IGFzc3VtZXMgdGhhdCB0aGUgb3V0bGllcnMgYXJlIG5vdCBpbmZvcm1hdGl2ZSBhbmQgbWF5IGxlYWQgdG8gYmlhc2VkIGVzdGltYXRlcy4gCgoyLiBSb2J1c3QgUmVncmVzc2lvbiBNZXRob2RzOiBHaXZlbiB0aGUgbnVtYmVyIG9mIG91dGxpZXJzIGFuZCB0aGVpciBwb3RlbnRpYWwgaW5mbHVlbmNlIG9uIHRoZSBtb2RlbCwgYSByb2J1c3QgcmVncmVzc2lvbiBtZXRob2QgbWlnaHQgYmUgYSBnb29kIGNob2ljZS4gVGhlc2UgbWV0aG9kcyBhcmUgbGVzcyBzZW5zaXRpdmUgdG8gb3V0bGllcnMgYW5kIGNhbiBvZnRlbiBwcm92aWRlIGJldHRlciBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlIHdoZW4gb3V0bGllcnMgYXJlIHByZXNlbnQuCgozLiBPdGhlciBNYWNoaW5lIGxlYXJuaW5nIG1vZGVscyB3aGljaCBhcmUgbGVzcyBzZW5zZXRpdmUgdG8gb3V0bGllcnMuICh0byBiZSBlZGl0ZWQgKQoKCiMgMy4gRXhwbG9yZSBEYXRhCgpJbiB0aGlzIGZvcnRoY29taW5nIHNlY3Rpb24sIHdlIGRpdmUgaW50byB0aGUgZXhwbG9yYXRpb24gb2Ygb3VyIGRhdGFzZXQsIGRpc3Rpbmd1aXNoaW5nIHZhcmlhYmxlcyBpbnRvIGZvdXIgZGlzdGluY3QgY2F0ZWdvcmllcy4KCkZpcnN0bHksIHdlIGhhdmUgJ3llYXInIHdoaWNoIGZhbGxzIHVuZGVyICoqdGltZS1kZXBlbmRlbnQgdmFyaWFibGVzKiosIG1hcHBpbmcgdGhlIHRlbXBvcmFsIGV2b2x1dGlvbiBvZiBvdXIgZGF0YS4KClNlY29uZGx5LCB3ZSBoYXZlIGEgc2V0IG9mICoqZ2VvZ3JhcGhpY2FsIGFuZCBtZXRlb3JvbG9naWNhbCB2YXJpYWJsZXMqKi4gVGhlc2UgaW5jbHVkZSAnY29udGluZW50JywgJ2NvdW50cnknLCAncG9wdWxhdGlvbicsIGFuZCBhIHJhbmdlIG9mIHRlbXBlcmF0dXJlIHBhcmFtZXRlcnMgKG1pbmltdW0sIG1heGltdW0sIGF2ZXJhZ2UpIGFsb25nc2lkZSB0aGVpciB0cmFuc2Zvcm1hdGlvbnMsIG9mZmVyaW5nIHVzIGluc2lnaHRzIGludG8gcmVnaW9uYWwgYW5kIGVudmlyb25tZW50YWwgaW5mbHVlbmNlcy4KCk91ciB0aGlyZCBjYXRlZ29yeSBicmluZ3MgdG9nZXRoZXIgKipzb2NpYWwgYW5kIGVjb25vbWljIHZhcmlhYmxlcyoqIHN1Y2ggYXMgJ2xpZmUgZXhwZWN0YW5jeScsICdHRFAnLCBhbmQgJ0dEUCBwZXIgY2FwaXRhJy4gVGhlc2UsIGFsb25nIHdpdGggdGhlaXIgcmVzcGVjdGl2ZSB0cmFuc2Zvcm1hdGlvbnMsIGNhcHR1cmUgdGhlIHNvY2lvLWVjb25vbWljIGJhY2tkcm9wIGFnYWluc3Qgd2hpY2ggd2Ugb2JzZXJ2ZSBvdXIgZGF0YS4KCkxhc3RseSwgb3VyIGZvdXJ0aCBjYXRlZ29yeSBjb21wcmlzZXMgKipkZW1vZ3JhcGhpYyB2YXJpYWJsZXMqKiwgbmFtZWx5ICdzZXgnIGFuZCAnYWdlJywgYWxsb3dpbmcgdXMgdG8gZXhhbWluZSB0aGUgaW5mbHVlbmNlIG9mIHRoZXNlIHZpdGFsIGRlbW9ncmFwaGljcyBvbiBvdXIgZGF0YS4KCk1vcmVvdmVyLCB3ZSBoYXZlIGlkZW50aWZpZWQgdGhyZWUgcG90ZW50aWFsIHRhcmdldCB2YXJpYWJsZXMgZm9yIG91ciBzdHVkeTogJ3N1aWNpZGVfcmF0aW8nLCAnbG9nX3N1aWNpZGVfcmF0aW8nLCBhbmQgJ3NxcnRfc3VpY2lkZV9yYXRpbycuIE9mIHRoZXNlLCAnbG9nX3N1aWNpZGVfcmF0aW8nIGhhcyBiZWVuIGZvdW5kIHRvIGJlIGhpZ2hseSBlZmZlY3RpdmUgaW4gbWluaW1pemluZyB0aGUgaW1wYWN0IG9mIG91dGxpZXJzLiBZZXQsIG91ciBleHBsb3JhdGlvbiB3b24ndCBiZSBsaW1pdGVkIHRvIGl0LiBXZSBhaW0gdG8gdGhvcm91Z2hseSBpbnZlc3RpZ2F0ZSB0aGUgaW1wYWN0IG9mIGFsbCB2YXJpYWJsZXMgb24gZWFjaCBwb3RlbnRpYWwgdGFyZ2V0IHVudGlsIHdlIGVtYmFyayBvbiB0aGUgbW9kZWxpbmcgcGhhc2UsIHdoZXJlIHdlIHdpbGwgc2VsZWN0IHRoZSBtb3N0IHN1aXRhYmxlIHRhcmdldCB2YXJpYWJsZSBmb3Igb3VyIHByZWRpY3RpdmUgbW9kZWwuCgoKYGBge3J9CmdsaW1wc2UoZGF0YSkKYGBgCgpgYGB7cn0KY29sdW1uX25hbWUgPC0gY29sbmFtZXMoZGF0YSkKYGBgCgojIyAzLjEgIFRpbWUtRGVwZW5kZW50IAoKCmBgYHtyfQojIHRoZSBnbG9iYWwgcmF0ZSBvdmVyIHRoZSB0aW1lIHBlcmlvZCB3aWxsIGJlIHVzZWZ1bDoKZ2xvYmFsX2F2ZXJhZ2UgPC0gKHN1bShhcy5udW1lcmljKGRhdGEkc3VpY2lkZXNfbm8pKSAvIHN1bShhcy5udW1lcmljKGRhdGEkcG9wdWxhdGlvbikpKSAqIDEwMDAwMAoKZGF0YSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpemUocG9wdWxhdGlvbiA9IHN1bShwb3B1bGF0aW9uKSwgCiAgICAgICAgICAgIHN1aWNpZGVzID0gc3VtKHN1aWNpZGVzX25vKSwgCiAgICAgICAgICAgIHN1aWNpZGVzX3Blcl8xMDBrID0gKHN1aWNpZGVzIC8gcG9wdWxhdGlvbikgKiAxMDAwMDApICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBzdWljaWRlc19wZXJfMTAwaykpICsgCiAgZ2VvbV9saW5lKGNvbCA9ICJyZWQiLCBsaW5ld2lkdGggPSAxKSArIAogIGdlb21fcG9pbnQoY29sID0gInJlZCIsIHNpemUgPSAyKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGdsb2JhbF9hdmVyYWdlLCBsaW5ldHlwZSA9IDIsIGNvbG9yID0gImdyZXkzNSIsIGxpbmV3aWR0aCA9IDEpICsKICBsYWJzKHRpdGxlID0gIkdsb2JhbCBTdWljaWRlcyAocGVyIDEwMGspIiwKICAgICAgIHN1YnRpdGxlID0gIlRyZW5kIG92ZXIgdGltZSwgMTk4NSAtIDIwMTUuIiwKICAgICAgIHggPSAiWWVhciIsIAogICAgICAgeSA9ICJTdWljaWRlcyBwZXIgMTAwayIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTg1LCAyMDE1LCAyKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEwLCAyMCkpCmBgYApUaGUgcGxvdCBhYm92ZSB5aWVsZHMgc2V2ZXJhbCBpbnNpZ2h0ZnVsIG9ic2VydmF0aW9uczoKCiogVGhlIGhpZ2hlc3Qgc3VpY2lkZSByYXRlIHJlY29yZGVkIHdhcyAxOC43IGRlYXRocyBwZXIgMTAwayBwb3B1bGF0aW9uLCBvYnNlcnZlZCBpbiAxOTk1LgoqIFRoaXMgcmF0ZSBoYXMgc2VlbiBhIGNvbnNpc3RlbnQgZGVjcmVhc2UsIGZhbGxpbmcgdG8gMTMuNSBwZXIgMTAwayBwb3B1bGF0aW9uIGJ5IDIwMTUsIHdoaWNoIHRyYW5zbGF0ZXMgdG8gYSBzaWduaWZpY2FudCByZWR1Y3Rpb24gb2YgYWJvdXQgMjclLgoqIFByZXNlbnRseSwgdGhlIHJhdGVzIGFyZSBncmFkdWFsbHkgcmVncmVzc2luZyB0b3dhcmRzIHRoZSBmaWd1cmVzIHByZXZhbGVudCBwcmlvciB0byB0aGUgMTk5MHMuCgpIb3dldmVyLCBhIGNydWNpYWwgYXNwZWN0IHRvIHJlbWVtYmVyIGlzIHRoYXQgdGhlIGRhdGEgYXZhaWxhYmxlIGZyb20gdGhlIDE5ODBzIGlzIHJlbGF0aXZlbHkgc2NhcmNlLCB0aHVzIG1ha2luZyBpdCBkaWZmaWN1bHQgdG8gY29uY2x1c2l2ZWx5IHN0YXRlIHdoZXRoZXIgdGhlc2UgcmF0ZXMgd2VyZSBhbiBhY2N1cmF0ZSByZWZsZWN0aW9uIG9mIHRoZSBnbG9iYWwgc3VpY2lkZSB0cmVuZHMgZHVyaW5nIHRoYXQgcGVyaW9kLgoKCiMjIyAzLjEuMSBXaHkgZGlkIHBlb3BsZSBraWxsZWQgdGhlbXNlbHZlcyBpbiAxOTk1PwoKYGBge3J9CmRhdGFfOTUgPC0gZGF0YSAlPiUKICBmaWx0ZXIoeWVhciA9PSAxOTk1KSAlPiUKICBncm91cF9ieShjb3VudHJ5KSAlPiUKICBzdW1tYXJpemUocG9wdWxhdGlvbiA9IHN1bShwb3B1bGF0aW9uKSwgCiAgICAgICAgICAgIHN1aWNpZGVzID0gc3VtKHN1aWNpZGVzX25vKSwgCiAgICAgICAgICAgIHN1aWNpZGVzX3Blcl8xMDBrID0gKHN1aWNpZGVzIC8gcG9wdWxhdGlvbikgKiAxMDAwMDApCgpkYXRhXzk1IDwtIGRhdGFfOTUgJT4lCiAgYXJyYW5nZShkZXNjKHN1aWNpZGVzX3Blcl8xMDBrKSkKCmhlYWQoZGF0YV85NSkKYGBgCgpEdXJpbmcgb3VyIGV4cGxvcmF0aW9uIG9mIG91dGxpZXJzLCB3ZSBvYnNlcnZlZCB0aGF0IEVhc3Rlcm4gRXVyb3BlYW4gY291bnRyaWVzIGhhdmUgc2lnbmlmaWNhbnRseSBoaWdoZXIgc3VpY2lkZSByYXRlcyBjb21wYXJlZCB0byBvdGhlciBuYXRpb25zLiBJbiBwYXJ0aWN1bGFyLCBpdCBhcHBlYXJzIHRoYXQgYSBzdWJzdGFudGlhbCBudW1iZXIgb2Ygc3VpY2lkZXMgaW4gMTk5NSB3ZXJlIHJlcG9ydGVkIGluIFJ1c3NpYS4KCmBgYHtyfQpkYXRhX3dpdGhvdXRfcnUgPC0gZGF0YSAlPiUKICBmaWx0ZXIoY291bnRyeSAhPSAiUnVzc2lhbiBGZWRlcmF0aW9uIikgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXplKHBvcHVsYXRpb24gPSBzdW0ocG9wdWxhdGlvbiksIAogICAgICAgICAgICBzdWljaWRlcyA9IHN1bShzdWljaWRlc19ubyksIAogICAgICAgICAgICBzdWljaWRlc19wZXJfMTAwayA9IChzdWljaWRlcyAvIHBvcHVsYXRpb24pICogMTAwMDAwKQoKCnllYXJseV9kYXRhIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXplKHBvcHVsYXRpb24gPSBzdW0ocG9wdWxhdGlvbiksIAogICAgICAgICAgICBzdWljaWRlcyA9IHN1bShzdWljaWRlc19ubyksIAogICAgICAgICAgICBzdWljaWRlc19wZXJfMTAwayA9IChzdWljaWRlcyAvIHBvcHVsYXRpb24pICogMTAwMDAwKQoKZGF0YV93aXRob3V0X3J1IDwtIGRhdGFfd2l0aG91dF9ydSAlPiUKICBtdXRhdGUoaW5jbHVzaW9uID0gIldpdGhvdXQgUnVzc2lhIikKCnllYXJseV9kYXRhIDwtIHllYXJseV9kYXRhICU+JQogIG11dGF0ZShpbmNsdXNpb24gPSAiV2l0aCBSdXNzaWEiKQoKY29tYmluZWRfZGF0YSA8LSBiaW5kX3Jvd3MoZGF0YV93aXRob3V0X3J1LCB5ZWFybHlfZGF0YSkKCmdncGxvdChjb21iaW5lZF9kYXRhLCBhZXMoeCA9IHllYXIsIHkgPSBzdWljaWRlc19wZXJfMTAwaywgY29sb3IgPSBpbmNsdXNpb24pKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdOdW1iZXIgb2YgU3VpY2lkZXMgZm9yIGV2ZXJ5IDEwMGsgcGVvcGxlJywgCiAgICB4ID0gJ1llYXInLCAKICAgIHkgPSAnU3VpY2lkZSBSYXRpbyBwZXIgMTAwaycsCiAgICBjb2xvciA9ICJDb3VudHJ5IEluY2x1c2lvbiIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIldpdGhvdXQgUnVzc2lhIiA9ICJibHVlIiwgIldpdGggUnVzc2lhIiA9ICJyZWQiKSkKYGBgClRoZSBzaWduaWZpY2FudCBpbmNyZWFzZSBpbiBzdWljaWRlIHJhdGVzIG9ic2VydmVkIGluIFJ1c3NpYSBpbiAxOTk1IGNhbiBiZSBhdHRyaWJ1dGVkIHRvIGEgY29tYmluYXRpb24gb2Ygc2V2ZXJhbCBmYWN0b3JzLCBlYWNoIGNvbnRyaWJ1dGluZyB0byBhbiBvdmVyYWxsIHNlbnNlIG9mIGRlc3BhaXIgYW5kIGluc3RhYmlsaXR5IHdpdGhpbiB0aGUgcG9wdWxhdGlvbjoKCioqRWNvbm9taWMgQ3Jpc2lzKio6IFRoZSBwZXJpb2QgbWFya2VkIFJ1c3NpYSdzIGNoYWxsZW5naW5nIHRyYW5zaXRpb24gZnJvbSBhIGNlbnRyYWxseSBwbGFubmVkIGVjb25vbXkgdG8gYSBmcmVlLW1hcmtldCBzeXN0ZW0uIFRoaXMgc2hpZnQgcmVzdWx0ZWQgaW4gc3Vic3RhbnRpYWwgZWNvbm9taWMgdHVybW9pbCBjaGFyYWN0ZXJpemVkIGJ5IGhpZ2ggdW5lbXBsb3ltZW50IHJhdGVzLCByYW1wYW50IGluZmxhdGlvbiwgYW5kIGdlbmVyYWwgZWNvbm9taWMgdW5jZXJ0YWludHkuIEFzIG51bWVyb3VzIHN0dWRpZXMgaGF2ZSBpbmRpY2F0ZWQsIHN1Y2ggZWNvbm9taWMgaGFyZHNoaXBzIGNhbiBncmVhdGx5IGluY3JlYXNlIHN0cmVzcyBsZXZlbHMgd2l0aGluIHRoZSBwb3B1bGF0aW9uLCB0aGVyZWJ5IGxlYWRpbmcgdG8gaGlnaGVyIHN1aWNpZGUgcmF0ZXMuCgoqKlBvbGl0aWNhbCBJbnN0YWJpbGl0eSoqOiBUaGUgZGlzc29sdXRpb24gb2YgdGhlIFNvdmlldCBVbmlvbiBpbiAxOTkxIHByZWNpcGl0YXRlZCBhIHNlcmllcyBvZiByYWRpY2FsIHBvbGl0aWNhbCBhbmQgc29jaWV0YWwgY2hhbmdlcy4gVGhlIGVuc3VpbmcgdW5jZXJ0YWludHkgYW5kIHRoZSByZXN1bHRhbnQgZmVlbGluZyBvZiBpbnNlY3VyaXR5IG1heSBoYXZlIGV4YWNlcmJhdGVkIHRoZSBhbHJlYWR5IHZvbGF0aWxlIHNpdHVhdGlvbiwgdGhlcmVieSBjb250cmlidXRpbmcgdG8gdGhlIHJpc2UgaW4gc3VpY2lkZSByYXRlcy4KCioqUmlzZSBpbiBBbGNvaG9saXNtKio6IER1cmluZyB0aGlzIHBlcmlvZCwgUnVzc2lhIGV4cGVyaWVuY2VkIGFuIGluY3JlYXNlIGluIGFsY29ob2wgYWJ1c2UsIGEgcHJvYmxlbSB0aGF0IGhhcyBoaXN0b3JpY2FsbHkgYmVlbiBhIGNoYWxsZW5nZSBmb3IgdGhlIGNvdW50cnkuIEEgd2VsbC1lc3RhYmxpc2hlZCBib2R5IG9mIHJlc2VhcmNoIHNob3dzIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gYWxjb2hvbCBhYnVzZSBhbmQgc3VpY2lkZSByYXRlcy4gVGhlIHNwaWtlIGluIGFsY29ob2xpc20gZHVyaW5nIHRoaXMgdGltZSBtaWdodCBoYXZlLCB0aGVyZWZvcmUsIGJlZW4gYSBzaWduaWZpY2FudCBjb250cmlidXRvciB0byB0aGUgc3VpY2lkZSByYXRlcyBbbGlua3NdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzE2NDI3NjcvKS4KCkl0IGlzIGVzc2VudGlhbCB0byBub3RlIHRoYXQgd2hpbGUgdGhlc2UgZmFjdG9ycyBhcmUgZGlzdGluY3QsIHRoZXkgYXJlIGludGVycmVsYXRlZCBhbmQgbGlrZWx5IGV4YWNlcmJhdGVkIGVhY2ggb3RoZXIncyBlZmZlY3RzIG9uIHRoZSBwb3B1bGF0aW9uJ3MgbWVudGFsIGhlYWx0aC4gCgoKCgojIyAzLjIgR2VvZ3JhcGhpY2FsIGFuZCBNZXRlb3JvbG9naWNhbAoKCgoKIyMjIDMuMi4xIFBvcHVsYXRpb24KCiMjIyMgMy4yLjEuMSBVbml2YXJpYXRlIEFuYWx5c2lzCgpgYGB7cn0KZGF0YSRzY2FsZWRfcG9wdWxhdGlvbiA8LSAoZGF0YSRwb3B1bGF0aW9uIC0gbWluKGRhdGEkcG9wdWxhdGlvbikpLyhtYXgoZGF0YSRwb3B1bGF0aW9uKS1taW4oZGF0YSRwb3B1bGF0aW9uKSkKZGF0YSRzY2FsZWRfbG9nX3BvcHVsYXRpb24gPC0gKGRhdGEkbG9nX3BvcHVsYXRpb24gLSBtaW4oZGF0YSRsb2dfcG9wdWxhdGlvbikpIC8gKG1heChkYXRhJGxvZ19wb3B1bGF0aW9uKSAtIG1pbihkYXRhJGxvZ19wb3B1bGF0aW9uKSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZ3JpZEV4dHJhKQoKIyBPcmlnaW5hbCBQb3B1bGF0aW9uCnAxIDwtIGdncGxvdChkYXRhLCBhZXMocG9wdWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xMDAwLCBmaWxsPSJza3libHVlIiwgY29sb3I9ImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIFBvcHVsYXRpb24iLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICJDb3VudCIpCgpwMiA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgeSA9IHBvcHVsYXRpb24pKSArCiAgZ2VvbV9ib3hwbG90KGZpbGw9ImxpZ2h0Z3JlZW4iLCBjb2xvcj0iYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBQb3B1bGF0aW9uIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIikKCiMgU2NhbGVkIFBvcHVsYXRpb24KcDMgPC0gZ2dwbG90KGRhdGEsIGFlcyhzY2FsZWRfcG9wdWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0wLjAxLCBmaWxsPSJza3libHVlIiwgY29sb3I9ImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIFNjYWxlZCBQb3B1bGF0aW9uIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiQ291bnQiKQoKcDQgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gIiIsIHkgPSBzY2FsZWRfcG9wdWxhdGlvbikpICsKICBnZW9tX2JveHBsb3QoZmlsbD0ibGlnaHRncmVlbiIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkJveCBQbG90IG9mIFNjYWxlZCBQb3B1bGF0aW9uIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIikKCiMgU2NhbGVkIExvZyBQb3B1bGF0aW9uCnA1IDwtIGdncGxvdChkYXRhLCBhZXMoc2NhbGVkX2xvZ19wb3B1bGF0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuMDEsIGZpbGw9InNreWJsdWUiLCBjb2xvcj0iYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gb2YgU2NhbGVkIExvZyBQb3B1bGF0aW9uIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiQ291bnQiKQoKcDYgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gIiIsIHkgPSBzY2FsZWRfbG9nX3BvcHVsYXRpb24pKSArCiAgZ2VvbV9ib3hwbG90KGZpbGw9ImxpZ2h0Z3JlZW4iLCBjb2xvcj0iYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBTY2FsZWQgTG9nIFBvcHVsYXRpb24iLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiKQoKIyBBcnJhbmdlIHRoZSBwbG90cyBpbiBhIGdyaWQKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBwNSwgcDYsIG5jb2wgPSAyKQoKYGBgCkZyb20gdGhlc2UgdmlzdWFsaXphdGlvbnMsIHdlIGNhbiBvYnNlcnZlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcG9wdWxhdGlvbiB2YXJpYWJsZSBiZWNvbWVzIHNpZ25pZmljYW50bHkgbGVzcyBza2V3ZWQgYWZ0ZXIgYXBwbHlpbmcgYm90aCBtaW4tbWF4IHNjYWxpbmcgYW5kIGEgbG9nIHRyYW5zZm9ybWF0aW9uLiBUaGUgcmVzdWx0aW5nIHNjYWxlZCBsb2cgcG9wdWxhdGlvbiBhcHBlYXJzIG1vcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIHNob3dzIGZld2VyIG91dGxpZXJzIGNvbXBhcmVkIHRvIHRoZSBvcmlnaW5hbCBhbmQgc2NhbGVkIHBvcHVsYXRpb24uIFRoZXJlZm9yZSwgdXNpbmcgdGhlICdzY2FsZWRfbG9nX3BvcHVsYXRpb24nIHZhcmlhYmxlIGluIG91ciBhbmFseXNlcyBzaG91bGQgeWllbGQgbW9yZSByb2J1c3QgcmVzdWx0cy4KCgojIyMjIDMuMi4xLjIgQml2YXJpYXRlIEFuYWx5c2lzCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGludmVzdGlnYXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIG91ciBjYW5kaWRhdGUgdGFyZ2V0IHZhcmlhYmxlcyBhbmQgc2NhbGVkX2xvZ19wb3B1bGF0aW9uLiBTaW5jZSBvdXIgc3VpY2lkZV9yYXRpbyB2YXJpYWJsZSB3YXMgZGVyaXZlZCBmcm9tIHRoZSByYXRpbyBvZiBzdWljaWRlX25vIHRvIHBvcHVsYXRpb24sIG91ciBhbmFseXNpcyBmb2N1c2VzIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzY2FsZWRfbG9nX3BvcHVsYXRpb24gYW5kIHRoZSB0aHJlZSB0cmFuc2Zvcm1hdGlvbnMgb2Ygc3VpY2lkZV9ubzogbmFtZWx5LCBzdWljaWRlX25vLCBsb2dfc3VpY2lkZV9ubywgYW5kIHNxcnRfc3VpY2lkZV9uby4KCmBgYHtyfQpkYXRhX2xvbmcgPC0gZGF0YSAlPiUKICBzZWxlY3Qoc2NhbGVkX2xvZ19wb3B1bGF0aW9uLCBzdWljaWRlc19ubywgbG9nX3N1aWNpZGVfbm8sIHNxcnRfc3VpY2lkZV9ubykgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtc2NhbGVkX2xvZ19wb3B1bGF0aW9uLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCgpnZ3Bsb3QoZGF0YV9sb25nLCBhZXMoeCA9IHNjYWxlZF9sb2dfcG9wdWxhdGlvbiwgeSA9IHZhbHVlLCBjb2xvcj0gInNreWJsdWUiKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWVfeSIpICsKICBsYWJzKHggPSAiU2NhbGVkIExvZyBQb3B1bGF0aW9uIiwKICAgICAgIHkgPSAiVmFsdWUiLAogICAgICAgdGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgU2NhbGVkIExvZyBQb3B1bGF0aW9uIHZzLiBUYXJnZXQgVmFyaWFibGVzIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpIAoKYGBgCkZyb20gdGhlc2Ugc2NhdHRlcnBsb3RzLCBpdCBhcHBlYXJzIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNjYWxlZF9sb2dfcG9wdWxhdGlvbiBhbmQgbG9nX3N1aWNpZGVfbm8gaXMgbW9yZSBsaW5lYXIgY29tcGFyZWQgdG8gdGhlIG90aGVyIHZhcmlhYmxlcy4gCgoKYGBge3J9CiMgTGlzdCBvZiB2YXJpYWJsZXMgdG8gY2FsY3VsYXRlIGNvcnJlbGF0aW9ucyB3aXRoIHNjYWxlZF9sb2dfcG9wdWxhdGlvbgp2YXJzIDwtIGMoInN1aWNpZGVzX25vIiwgImxvZ19zdWljaWRlX25vIiwgInNxcnRfc3VpY2lkZV9ubyIpCgojIENhbGN1bGF0ZSBjb3JyZWxhdGlvbnMKY29ycmVsYXRpb25zIDwtIHB1cnJyOjptYXBfZGJsKHZhcnMsIH5jb3IoZGF0YSRzY2FsZWRfbG9nX3BvcHVsYXRpb24sIGRhdGFbWy5dXSkpCgojIFByaW50IGNvcnJlbGF0aW9ucwpuYW1lcyhjb3JyZWxhdGlvbnMpIDwtIHZhcnMKcHJpbnQoY29ycmVsYXRpb25zKQpgYGAKCklzIGl0IHN1cnByaXNpbmcgdGhhdCB0aGVyZSBpcyBhIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHJhbnNmb3JtYXRpb24gb2YgcG9wdWxhdGlvbiBhbmQgc3VpY2lkZV9ubz8gTm90IGF0IGFsbCEKCk91ciBnb2FsIGlzIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBpbmRpdmlkdWFsIHRha2luZyB0aGVpciBvd24gbGlmZSwgd2hpY2ggd2UgY2FsY3VsYXRlIGJ5IGRpdmlkaW5nIHRoZSBzdWljaWRlX25vIGJ5IHRoZSBwb3B1bGF0aW9uIGluIGVhY2ggcm93LiBIb3dldmVyLCB0aGlzIG1lYW5zIHdlIGNhbm5vdCBkaXJlY3RseSBleGFtaW5lIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzdWljaWRlX3JhdGlvIGFuZCBwb3B1bGF0aW9uLCBhcyB3ZSB1c2UgdGhlIHBvcHVsYXRpb24gZGlyZWN0bHkgdG8gY2FsY3VsYXRlIHRoZSByYXRpby4gSW5zdGVhZCwgd2UgbmVlZCB0byBhbmFseXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBwb3B1bGF0aW9uIGFuZCBzdWljaWRlX25vIGZvciBlYWNoIHJvdy4KCldoZW4gdGhlIHBvcHVsYXRpb24gb2YgZWFjaCByb3cgaW5jcmVhc2VzLCB0aGUgcHJvYmFiaWxpdHkgb2YgYSBoaWdoZXIgc3VpY2lkZV9ubyBhbHNvIHRlbmRzIHRvIGluY3JlYXNlLiBIb3dldmVyLCB0aGlzIGNvbXBhcmlzb24gbWF5IG5vdCBiZSBlbnRpcmVseSBhY2N1cmF0ZS4gU2hvdWxkIHdlIGRpc3JlZ2FyZCBwb3B1bGF0aW9uIGFsdG9nZXRoZXI/IEJlZm9yZSBtYWtpbmcgdGhhdCBkZWNpc2lvbiwgbGV0J3MgYWRkcmVzcyB0aGlzIHF1ZXN0aW9uOiBJcyB0aGVyZSBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgY291bnRyeSdzIHBvcHVsYXRpb24gYW5kIHN1aWNpZGVfcmF0aW8/IEluIG90aGVyIHdvcmRzLCBpcyB0aGUgcHJvYmFiaWxpdHkgb2YgYW4gaW5kaXZpZHVhbCB0YWtpbmcgdGhlaXIgb3duIGxpZmUgaGlnaGVyIGluIGNvdW50cmllcyB3aXRoIGEgbGFyZ2VyIHBvcHVsYXRpb24/CgpUbyBleHBsb3JlIHRoaXMsIHdlIGhhdmUgc3RyYXRpZmllZCB0aGUgcG9wdWxhdGlvbiBvZiBlYWNoIGNvdW50cnkgaW50byB0aHJlZSBjYXRlZ29yaWVzIChiaWcsIG1lZGl1bSwgYW5kIHNtYWxsKSBmb3IgZWFjaCB5ZWFyLiBUaGlzIGFsbG93cyB1cyB0byBleGFtaW5lIHdoZXRoZXIgdGhlcmUgaXMgYSBub3RhYmxlIGRpZmZlcmVuY2UgaW4gc3VpY2lkZV9yYXRpbyBiYXNlZCBvbiB0aGUgc2l6ZSBvZiB0aGUgcG9wdWxhdGlvbi4KCkJlZm9yZSBtb3ZpbmcgZm9yd2FyZCwgbGV0J3MgY3JlYXRlIGEgZGF0YWZyYW1lIHRoYXQgY2FsY3VsYXRlcyB0aGUgcG9wdWxhdGlvbiBvZiBlYWNoIGNvdW50cnkgb3ZlciB0aGUgeWVhcnMuCmBgYHtyfQpwb3BfZGF0YSA8LSBkYXRhICU+JQogIGdyb3VwX2J5KHllYXIsIGNvdW50cnkpICU+JQogIHN1bW1hcml6ZShwb3B1bGF0aW9uID0gc3VtKHBvcHVsYXRpb24sIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpCmBgYAoKV2UgYWltIHRvIGVzdGFibGlzaCB0aHJlc2hvbGRzIGZvciBlYWNoIHllYXIgdG8gY2xhc3NpZnkgY291bnRyaWVzIGludG8gZm91ciBjYXRlZ29yaWVzOiB2ZXJ5IHNtYWxsLCBzbWFsbCwgbWVkaXVtLCBhbmQgbGFyZ2UuIEl0IGlzIGltcG9ydGFudCB0byBpZGVudGlmeSBuYXR1cmFsIGdhcHMgaW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBwb3B1bGF0aW9uIGZvciBlYWNoIGNvdW50cnkgd2l0aGluIGEgZ2l2ZW4geWVhci4gSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgYSBtZWRpYW4tYmFzZWQgYXBwcm9hY2ggaXMgbm90IHN1aXRhYmxlIHNpbmNlIGl0IHdvdWxkIHJlc3VsdCBpbiB0aHJlZSBjYXRlZ29yaWVzIG9mIGVxdWFsIHNpemUuIEFkZGl0aW9uYWxseSwgd2UgbmVlZCB0byBjb21wYXJlIHRoZSBwb3B1bGF0aW9uIG9mIGVhY2ggY291bnRyeSB3aXRoIHRoZSBwb3B1bGF0aW9ucyBvZiBvdGhlciBjb3VudHJpZXMgd2l0aGluIHRoZSBzYW1lIHllYXIsIGFzIGEgY291bnRyeSBtYXkgbm90IGJlIGRlbnNlbHkgcG9wdWxhdGVkIGF0IHByZXNlbnQgYnV0IGNvdWxkIGV4cGVyaWVuY2UgYSBzaWduaWZpY2FudCBpbmNyZWFzZSBpbiBwb3B1bGF0aW9uIGluIHRoZSBmdXR1cmUuCgpUbyBhY2NvbXBsaXNoIHRoaXMsIHdlIGVtcGxveSB0aGUgSmVua3MgbmF0dXJhbCBicmVha3MgY2xhc3NpZmljYXRpb24gbWV0aG9kIGZvciBlYWNoIHllYXIuIFRoaXMgbWV0aG9kIGFpbXMgdG8gbWluaW1pemUgdGhlIHZhcmlhbmNlIHdpdGhpbiBjbGFzc2VzIHdoaWxlIG1heGltaXppbmcgdGhlIHZhcmlhbmNlIGJldHdlZW4gY2xhc3Nlcy4gSXQgaW52b2x2ZXMgYW4gaXRlcmF0aXZlIHByb2Nlc3MgdGhhdCByZWFsbG9jYXRlcyBvYnNlcnZhdGlvbnMgZnJvbSBvbmUgY2xhc3MgdG8gYW5vdGhlciB1bnRpbCBhbiBvcHRpbWFsIGFycmFuZ2VtZW50IGlzIGFjaGlldmVkLgoKYGBge3J9CiMgTG9hZCByZXF1aXJlZCBsaWJyYXJ5CmxpYnJhcnkoY2xhc3NJbnQpCgojIERlZmluZSBhIGZ1bmN0aW9uIHRvIGFwcGx5IEZpc2hlci1KZW5rcyBtZXRob2QgZm9yIGJpbm5pbmcKZmlzaGVyX2plbmtzIDwtIGZ1bmN0aW9uKHgpIHsKICBiaW5zIDwtIGNsYXNzSW50ZXJ2YWxzKHgsIG4gPSA1LCBzdHlsZSA9ICJmaXNoZXIiKSRicmtzCiAgY3V0KHgsIGJyZWFrcyA9IGJpbnMsIGxhYmVscyA9IGMoIlZlcnlfU21hbGwiLCJTbWFsbCIsICJNZWRpdW0iLCAiTGFyZ2UiLCJWZXJ5X0xhcmdlIiApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpCn0KCiMgQWRkIHRoZSBuZXcgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lCnBvcF9kYXRhIDwtIHBvcF9kYXRhICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIG11dGF0ZShwb3B1bGF0aW9uX2JpbmVfamVua3MgPSBmaXNoZXJfamVua3MocG9wdWxhdGlvbikpCgojIFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBuZXcgZGF0YQpoZWFkKHBvcF9kYXRhKQpgYGAKTm93LCBsZXQncyBleGFtaW5lIHRoZSBwb3B1bGF0aW9uIGNhdGVnb3JpZXMgd2UndmUgY3JlYXRlZDoKCmBgYHtyfQojIFRhYnVsYXRlIHRoZSBjYXRlZ29yaWVzCnBvcF9kaXN0cmlidXRpb24gPC0gdGFibGUocG9wX2RhdGEkcG9wdWxhdGlvbl9iaW5lX2plbmtzKQpwcmludChwb3BfZGlzdHJpYnV0aW9uKQpgYGAKRnJvbSB0aGUgb3V0cHV0LCB3ZSBub3RpY2UgdGhhdCB0aGUgY2F0ZWdvcnkgIlZlcnkgTGFyZ2UiIGNvbnNpc3RzIG9mIG9ubHkgb25lIGNvdW50cnksIHRoZSBVU0EuIFRvIGhhdmUgbW9yZSBiYWxhbmNlZCBjYXRlZ29yaWVzLCB3ZSdsbCBjb21iaW5lIHRoZSBVU0Egd2l0aCB0aGUgIkxhcmdlIiBjYXRlZ29yeSBhbmQgcmVuYW1lIGFjY29yZGluZ2x5OgoKCmBgYHtyfQojIEFkanVzdCB0aGUgY2F0ZWdvcmllcwpwb3BfZGF0YSA8LSBwb3BfZGF0YSAlPiUKICBtdXRhdGUocG9wdWxhdGlvbl9iaW5lX2plbmtzID0gaWZlbHNlKHBvcHVsYXRpb25fYmluZV9qZW5rcyA9PSAiVmVyeV9MYXJnZSIsICJMYXJnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocG9wdWxhdGlvbl9iaW5lX2plbmtzKSkpCgojIENoZWNrIHRoZSBkaXN0cmlidXRpb24gYWZ0ZXIgYWRqdXN0bWVudAphZGp1c3RlZF9wb3BfZGlzdHJpYnV0aW9uIDwtIHRhYmxlKHBvcF9kYXRhJHBvcHVsYXRpb25fYmluZV9qZW5rcykKcHJpbnQoYWRqdXN0ZWRfcG9wX2Rpc3RyaWJ1dGlvbikKYGBgCgpgYGB7cn0KcG9wX2RhdGEKYGBgCgoKCmBgYHtyfQojIEpvaW4gdGhlIHBvcHVsYXRpb24gY2F0ZWdvcmllcyBpbnRvIG91ciBvcmlnaW5hbCBkYXRhCmRhdGE8LSBkYXRhICU+JQogIGxlZnRfam9pbihwb3BfZGF0YSU+JQogIHNlbGVjdChjb3VudHJ5LHllYXIscG9wdWxhdGlvbl9iaW5lX2plbmtzKSxieSA9IGMoInllYXIiLCAiY291bnRyeSIpKQpgYGAKV2UgYWxzbyBiaW4gdGhlIHBvcHVsYXRpb24gYmFzZWQgb24gdGhlIG1lZGlhbiBhbmQgY29tcGFyZSBpdHMgcGVyZm9ybWFuY2Ugd2l0aCB0aGUgSmVua3MgbmF0dXJhbCBicmVha3MgY2xhc3NpZmljYXRpb24gbWV0aG9kIGluIG91ciBtb2RlbC4KYGBge3J9CmRhdGFfc3VtIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSwgeWVhcikgJT4lCiAgc3VtbWFyaXNlKHBvcHVsYXRpb24gPSBzdW0ocG9wdWxhdGlvbikpCgp0aHJlc2hvbGRzIDwtIHF1YW50aWxlKGRhdGFfc3VtJHBvcHVsYXRpb24sIHByb2JzID0gYygwLjI1LCAwLjUsIDAuNzUpKQoKIyBBc3NpZ24gZWFjaCBjb3VudHJ5LXllYXIgcGFpciB0byBhIHBvcHVsYXRpb24gY2F0ZWdvcnkKZGF0YV9iaW5uZWQgPC0gZGF0YV9zdW0gJT4lCiAgbXV0YXRlKAogICAgcG9wdWxhdGlvbl9iaW5lX21lZGlhbiA9IGNhc2Vfd2hlbigKICAgICAgcG9wdWxhdGlvbiA8PSB0aHJlc2hvbGRzWzFdIH4gIlZlcnlfU21hbGwiLAogICAgICBwb3B1bGF0aW9uID4gdGhyZXNob2xkc1sxXSAmIHBvcHVsYXRpb24gPD0gdGhyZXNob2xkc1syXSB+ICJTbWFsbCIsCiAgICAgIHBvcHVsYXRpb24gPiB0aHJlc2hvbGRzWzJdICYgcG9wdWxhdGlvbiA8PSB0aHJlc2hvbGRzWzNdIH4gIk1lZGl1bSIsCiAgICAgIFRSVUUgfiAiTGFyZ2UiCiAgICApCiAgKQoKZGF0YSA8LSBkYXRhICU+JQogIGxlZnRfam9pbihkYXRhX2Jpbm5lZCU+JQogIHNlbGVjdChjb3VudHJ5LHllYXIscG9wdWxhdGlvbl9iaW5lX21lZGlhbiksYnkgPSBjKCJ5ZWFyIiwgImNvdW50cnkiKSkKYGBgCkluIHRoaXMgc3RlcCwgd2Ugd2lsbCBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBsb2dfc3VpY2lkZV9yYXRpbyAoY2hvc2VuIGJlY2F1c2UgaXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24pIGFuZCBwb3B1bGF0aW9uX2JpbmRlX2plbmtzLgoKTGV0J3Mgc3RhcnQgYnkgZXhhbWluaW5nIHRoZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciBib3RoIGxvZ19zdWljaWRlX3JhdGlvIGFuZCBzdWljaWRlX3JhdGlvLgoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkocG9wdWxhdGlvbl9iaW5lX2plbmtzKSAlPiUKICBzdW1tYXJpc2UoCiAgICBtZWFuX3N1aWNpZGVfcmF0aW8gPSBtZWFuKGxvZ19zdWljaWRlX3JhdGlvLCBuYS5ybSA9IFRSVUUpLAogICAgbWVkaWFuX3N1aWNpZGVfcmF0aW8gPSBtZWRpYW4obG9nX3N1aWNpZGVfcmF0aW8sIG5hLnJtID0gVFJVRSksCiAgICBtaW5fc3VpY2lkZV9yYXRpbyA9IG1pbihsb2dfc3VpY2lkZV9yYXRpbywgbmEucm0gPSBUUlVFKSwKICAgIG1heF9zdWljaWRlX3JhdGlvID0gbWF4KGxvZ19zdWljaWRlX3JhdGlvLCBuYS5ybSA9IFRSVUUpCiAgKQpgYGAKYGBge3J9CmRhdGFfZ3JvdXBlZCA8LSBkYXRhICU+JQogIGdyb3VwX2J5KHBvcHVsYXRpb25fYmluZV9qZW5rcykgJT4lCiAgc3VtbWFyaXNlKAogICAgc3VpY2lkZV9yYXRpbyA9IHN1bShzdWljaWRlc19ubykgLyAoc3VtKHBvcHVsYXRpb24pICkKICApCgpnZ3Bsb3QoZGF0YV9ncm91cGVkLCBhZXMoeCA9IHBvcHVsYXRpb25fYmluZV9qZW5rcywgeSA9IHN1aWNpZGVfcmF0aW8sIGZpbGwgPSBwb3B1bGF0aW9uX2JpbmVfamVua3MpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJQb3B1bGF0aW9uIEJpbiIsIHkgPSAiU3VpY2lkZSBSYXRpbyIsIHRpdGxlID0gIlN1aWNpZGUgUmF0aW8gZm9yIGVhY2ggUG9wdWxhdGlvbiBCaW4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikKYGBgClRoZSBhYm92ZSBwbG90IHN1Z2dlc3RzIHRoYXQgdGhlIHN1aWNpZGUgcmF0aW8gaW4gTGFyZ2UgY291bnRpcmVzIGlzIG11Y2ggaGlnaGVyLiBOb3cgd2UgdXNlIHN0YXRpc3RpY2FsIHRlc3RzIHRvIHNlZSBpZiB0aGlzIGRpZmZlcmVuY2UgaXMgc3RhdGlzdGljYWx5IHNpZ2luaWZ0aWNhbnQuIFNpbmNlIHdlIGhhdmUgbW9yZSB0aGFuIHR3byBzYW1wbGVzIHdlIGNhbiB1c2UgQU5PVkEuCkl0J3MgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB0aGF0IEFOT1ZBIG1ha2VzIHNldmVyYWwgYXNzdW1wdGlvbnMsIGluY2x1ZGluZyB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB0aGF0IHRoZSB2YXJpYW5jZXMgYXJlIGVxdWFsIGFjcm9zcyBncm91cHMsIGFuZCB0aGF0IHRoZSBvYnNlcnZhdGlvbnMgYXJlIGluZGVwZW5kZW50LiBXZSBzaG91bGQgY2hlY2sgdGhlc2UgYXNzdW1wdGlvbnMgYmVmb3JlIGludGVycHJldGluZyB0aGUgcmVzdWx0cy4KCgpgYGB7cn0KIyBGaXQgdGhlIG1vZGVsCnBvcF9hbm92YSA8LSBhb3YobG9nX3N1aWNpZGVfcmF0aW8gfiBwb3B1bGF0aW9uX2JpbmVfamVua3MsIGRhdGEgPSBkYXRhKQoKIyBSdW4gdGhlIEFOT1ZBCmFub3ZhX3Jlc3VsdCA8LSBhbm92YShwb3BfYW5vdmEpCgojIFByaW50IHRoZSByZXN1bHQKcHJpbnQoYW5vdmFfcmVzdWx0KQpgYGAKClRlc3RpbmcgTm9ybWFsaXR5IG9mIFJlc2lkdWFscyBBc3N1bXB0aW9uIGZvciBBTk9WQToKYGBge3J9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgcmVzaWR1YWxzCnJlc2lkdWFsc19kZiA8LSBkYXRhLmZyYW1lKHJlc2lkdWFscyA9IHJlc2lkdWFscyhwb3BfYW5vdmEpKQoKIyBDcmVhdGUgaGlzdG9ncmFtIG9mIHJlc2lkdWFscwpoaXN0X3Bsb3QgPC0gZ2dwbG90KHJlc2lkdWFsc19kZiwgYWVzKHggPSByZXNpZHVhbHMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICdzdGVlbGJsdWUnLCBjb2xvciA9ICdibGFjaycsIGJpbnMgPSAzMCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh4ID0gIlJlc2lkdWFscyIsIHkgPSAiRnJlcXVlbmN5IiwKICAgICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBSZXNpZHVhbHMiKQoKIyBDcmVhdGUgUS1RIHBsb3Qgb2YgcmVzaWR1YWxzCnFxX3Bsb3QgPC0gZ2dwbG90KHJlc2lkdWFsc19kZiwgYWVzKHNhbXBsZSA9IHJlc2lkdWFscykpICsKICBnZW9tX3FxKGNvbG9yID0gJ3N0ZWVsYmx1ZScpICsKICBnZW9tX3FxX2xpbmUoY29sb3IgPSAncmVkJykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJOb3JtYWwgUS1RIFBsb3QiLAogICAgICAgeCA9ICJUaGVvcmV0aWNhbCBRdWFudGlsZXMiLAogICAgICAgeSA9ICJTYW1wbGUgUXVhbnRpbGVzIikKCiMgQXJyYW5nZSB0aGUgcGxvdHMgc2lkZSBieSBzaWRlIHVzaW5nIHRoZSBncmlkRXh0cmEgcGFja2FnZQpsaWJyYXJ5KGdyaWRFeHRyYSkKZ3JpZC5hcnJhbmdlKGhpc3RfcGxvdCwgcXFfcGxvdCwgbmNvbCA9IDIpCmBgYAoKVGVzdGluZyBIb21vZ2VuZWl0eSBvZiBWYXJpYW5jZXMgQXNzdW1wdGlvbiBmb3IgQU5PVkE6CmBgYHtyfQpsZXZlbmVUZXN0KGxvZ19zdWljaWRlX3JhdGlvIH4gYWdlLCBkYXRhID0gZGF0YSkKYGBgCmBgYHtyfQpiYXJ0bGV0dC50ZXN0KGxvZ19zdWljaWRlX3JhdGlvIH4gYWdlLCBkYXRhID0gZGF0YSkKYGBgClRoZSBzbWFsbCBwLXZhbHVlcyBpbiBvdXIgdGVzdHMgbGVhZCB1cyB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgd2hpY2ggcG9zaXRzIHRoYXQgdGhlIHZhcmlhbmNlcyBhcmUgZXF1aXZhbGVudCBhY3Jvc3MgZGlmZmVyZW50IGdyb3Vwcy4gVGhpcyBpbXBsaWVzIHRoYXQgdGhlIHJlc3VsdHMgZnJvbSBhbiBBTk9WQSB0ZXN0IG1heSBub3QgYmUgcmVsaWFibGUgaW4gb3VyIGNhc2UuIFRoZXJlZm9yZSwgd2UgdHVybiB0byB0aGUgS3J1c2thbC1XYWxsaXMgdGVzdC4KClRoZSBLcnVza2FsLVdhbGxpcyB0ZXN0IGlzIGEgbm9uLXBhcmFtZXRyaWMgdGVjaG5pcXVlIHRoYXQgYXNzZXNzZXMgd2hldGhlciBzYW1wbGVzIG9yaWdpbmF0ZSBmcm9tIHRoZSBzYW1lIGRpc3RyaWJ1dGlvbi4gSXQgaXMgc3VpdGFibGUgZm9yIGNvbXBhcmluZyB0d28gb3IgbW9yZSBpbmRlcGVuZGVudCBzYW1wbGVzIG9mIGVxdWFsIG9yIGRpZmZlcmluZyBzYW1wbGUgc2l6ZXMsIGVmZmVjdGl2ZWx5IGV4dGVuZGluZyB0aGUgTWFubi1XaGl0bmV5IFUgdGVzdCwgd2hpY2ggaXMgdXRpbGl6ZWQgZm9yIGNvbXBhcmluZyBvbmx5IHR3byBncm91cHMuCgpVbmxpa2UgdGhlIG9uZS13YXkgQU5PVkEgYW5kIHQtdGVzdHMsIHRoZSBLcnVza2FsLVdhbGxpcyB0ZXN0IGRvZXMgbm90IHJlcXVpcmUgdGhlIHJlc2lkdWFscyB0byBiZSBub3JtYWxseSBkaXN0cmlidXRlZC4gQ29uc2VxdWVudGx5LCBpdCBjYW4gYmUgdXNlZCB3aXRoIGNvbnRpbnVvdXMgZGF0YSB0aGF0IGRvZXNuJ3QgZm9sbG93IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gSG93ZXZlciwgYWtpbiB0byBBTk9WQSwgaXQgZXZhbHVhdGVzIHdoZXRoZXIgdGhlIG1lYW4gcmFua3Mgb2YgdGhlIGdyb3VwcyBhcmUgZGlmZmVyZW50LCBub3QgdGhlIG1lYW5zIHRoZW1zZWx2ZXMuCgpUaGUgYXNzdW1wdGlvbnMgZm9yIHRoZSBLcnVza2FsLVdhbGxpcyB0ZXN0IGFyZToKCkluZGVwZW5kZW5jZTogT2JzZXJ2YXRpb25zIHdpdGhpbiBhbmQgYmV0d2VlbiBlYWNoIHNhbXBsZSBzaG91bGQgYmUgaW5kZXBlbmRlbnQsIGltcGx5aW5nIHRoYXQgb25lIG9ic2VydmF0aW9uJ3MgcHJlc2VuY2Ugb3IgYWJzZW5jZSBkb2Vzbid0IGluZmx1ZW5jZSBhbm90aGVyIG9ic2VydmF0aW9uJ3MgcHJlc2VuY2Ugb3IgYWJzZW5jZS4KCk9yZGluYWwgRGF0YTogVGhlIGRlcGVuZGVudCB2YXJpYWJsZSBtdXN0IGJlIG9yZGluYWwgYXQgYSBtaW5pbXVtLCBtZWFuaW5nIHRoYXQgdGhlIG9ic2VydmF0aW9ucyBjYW4gYmUgb3JkZXJlZC4gSXQgc2hvdWxkIGJlIHBvc3NpYmxlIHRvIHNheSB0aGF0IG9uZSBvYnNlcnZhdGlvbiBpcyBncmVhdGVyIHRoYW4sIGVxdWFsIHRvLCBvciBsZXNzIHRoYW4gYW5vdGhlciBvYnNlcnZhdGlvbi4KClNoYXBlIG9mIERpc3RyaWJ1dGlvbjogQWx0aG91Z2ggdGhlIEtydXNrYWwtV2FsbGlzIHRlc3QgZG9lc24ndCByZXF1aXJlIGEgc3BlY2lmaWMgZGF0YSBkaXN0cmlidXRpb24gbGlrZSBBTk9WQSBkb2VzLCBpdCBhc3N1bWVzIHRoYXQgdGhlIHNoYXBlIG9mIHRoZSBkaXN0cmlidXRpb24gaXMgaWRlbnRpY2FsIGZvciBlYWNoIGdyb3VwLiBXaGlsZSBncm91cHMgbWF5IGhhdmUgZGlmZmVyaW5nIG1lZGlhbnMsIHRoZWlyIGRpc3RyaWJ1dGlvbidzIG92ZXJhbGwgc2hhcGUgc2hvdWxkIGJlIHRoZSBzYW1lLgoKRm9yIG91ciBkYXRhLCB3ZSBhc3N1bWUgaW5kZXBlbmRlbmNlLiBPdXIgJ3BvcHVsYXRpb25fYmluJyB2YXJpYWJsZSBpcyBvcmRpbmFsLCB0aGVyZWJ5IGZ1bGZpbGxpbmcgdGhlIHNlY29uZCBhc3N1bXB0aW9uLiBUbyB2ZXJpZnkgdGhlIHRoaXJkIGFzc3VtcHRpb24sIHdlIG5lZWQgdG8gZXZhbHVhdGUgdGhlIGRpc3RyaWJ1dGlvbiBzaGFwZXMgdmlhIHBsb3RzLgoKYGBge3J9CiMgSGlzdG9ncmFtCmdncGxvdChkYXRhLCBhZXMoeCA9bG9nX3N1aWNpZGVfcmF0aW8pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgZmFjZXRfd3JhcCh+IHBvcHVsYXRpb25fYmluZV9qZW5rcykKCiMgRGVuc2l0eSBwbG90CmdncGxvdChkYXRhLCBhZXMoeCA9IGxvZ19zdWljaWRlX3JhdGlvKSkgKwogIGdlb21fZGVuc2l0eSgpICsKICBmYWNldF93cmFwKH4gcG9wdWxhdGlvbl9iaW5lX2plbmtzKQoKIyBRLVEgcGxvdApnZ3Bsb3QoZGF0YSwgYWVzKHNhbXBsZSA9IGxvZ19zdWljaWRlX3JhdGlvKSkgKwogIHN0YXRfcXEoKSArCiAgZmFjZXRfd3JhcCh+IHBvcHVsYXRpb25fYmluZV9qZW5rcykKYGBgCgpgYGB7cn0Ka3J1c2thbC50ZXN0KGxvZ19zdWljaWRlX3JhdGlvIH4gcG9wdWxhdGlvbl9iaW5lX2plbmtzLCBkYXRhID0gZGF0YSkKYGBgClRoZSByZXN1bHQgb2YgdGhlIEtydXNrYWwtV2FsbGlzIHRlc3QgYWxpZ25zIHdpdGggdGhhdCBvZiB0aGUgQU5PVkEuIFRoZSBvYnRhaW5lZCBwLXZhbHVlIGlzIHNpZ25pZmljYW50bHkgc21hbGwsIGluZGljYXRpbmcgdGhhdCB0aGUgc3VpY2lkZSByYXRlIGRpZmZlcnMgc2lnbmlmaWNhbnRseSBhbW9uZyBkaWZmZXJlbnQgcG9wdWxhdGlvbiBjYXRlZ29yaWVzLgoKCmBgYHtyfQojRmFjdG9yaXppbmcgb3VyIHR3byBuZXcgY29sdW1uIHBvcHVsYXRpb25fYmluZV9qZW5rcyBhbmQgcG9wdWxhdGlvbl9iaW5lX21lZGlhbgoKZGF0YSRwb3B1bGF0aW9uX2JpbmVfamVua3MgPC0gZmFjdG9yKGRhdGEkcG9wdWxhdGlvbl9iaW5lX2plbmtzLCAKICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlZlcnlfU21hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU21hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWVkaXVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhcmdlIikpCmRhdGEkcG9wdWxhdGlvbl9iaW5lX21lZGlhbiA8LSBmYWN0b3IoZGF0YSRwb3B1bGF0aW9uX2JpbmVfbWVkaWFuLCAKICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlZlcnlfU21hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU21hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWVkaXVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhcmdlIikpCmBgYAoKCiMjIyAzLjIuMiBDb3VudHJ5IGFuZCBDb250aW5lbnQKCiMjIyMgMy4yLjIuMSBVbml2YXJpYXRlIEFuYWx5c2lzCgpgYGB7cn0KIyBDb3VudCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBjb3VudHJ5CmNvdW50cnlfY291bnRzIDwtIGRhdGEgJT4lIAogIGdyb3VwX2J5KGNvdW50cnkpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKIyBDcmVhdGUgdGhlIGJhciBwbG90CmdncGxvdChjb3VudHJ5X2NvdW50cywgYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGNvdW50KSwgeSA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJDb3VudHJ5IiwgeSA9ICJOdW1iZXIgb2YgUm93cyIsIHRpdGxlID0gIk51bWJlciBvZiBSb3dzIGZvciBFYWNoIENvdW50cnkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTUpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgIyBSZW1vdmUgY291bnRyeSBuYW1lcwpgYGAKYGBge3J9CiMgSm9pbiB0aGUgZGF0YSB0byBhIG1hcAptYXBfZGF0YSA8LSBqb2luQ291bnRyeURhdGEyTWFwKGNvdW50cnlfY291bnRzLCBqb2luQ29kZSA9ICJOQU1FIiwgbmFtZUpvaW5Db2x1bW4gPSAiY291bnRyeSIpCgojIFNldCBtYXJnaW5zCnBhcihtYXIgPSBjKDAsIDAsIDEsIDApKQoKIyBQbG90IHRoZSBtYXAKbWFwQ291bnRyeURhdGEobWFwX2RhdGEsIAogICAgICAgICAgICAgICBuYW1lQ29sdW1uVG9QbG90ID0gImNvdW50IiwgCiAgICAgICAgICAgICAgIG1hcFRpdGxlID0gIk51bWJlciBvZiBSb3dzIGZvciBlYWNoIENvdW50cnkiLCAKICAgICAgICAgICAgICAgY29sb3VyUGFsZXR0ZSA9ICJibHVlcyIsICAjIGNoYW5nZSBjb2xvciBwYWxldHRlIGhlcmUKICAgICAgICAgICAgICAgb2NlYW5Db2wgPSAibGlnaHRibHVlIiwgCiAgICAgICAgICAgICAgIG1pc3NpbmdDb3VudHJ5Q29sID0gImdyZXk2NSIsIAogICAgICAgICAgICAgICBjYXRNZXRob2QgPSAicHJldHR5IikKYGBgCgpgYGB7cn0KIyBHZXQgdGhlIHVuaXF1ZSBjb3VudHJpZXMgcGVyIGNvbnRpbmVudCBpbiB0aGUgZGF0YQpjb250aW5lbnRfY291bnQgPC0gZGF0YSAlPiUKICBncm91cF9ieShjb250aW5lbnQpICU+JQogIHN1bW1hcmlzZShudW1fY291bnRyaWVzX2RhdGEgPSBuX2Rpc3RpbmN0KGNvdW50cnkpKQoKIyBBY3R1YWwgY291bnRyeSBjb3VudCBwZXIgY29udGluZW50CmFjdHVhbF9jb3VudCA8LSBkYXRhLmZyYW1lKAogIGNvbnRpbmVudCA9IGMoIkFmcmljYSIsICJBc2lhIiwgIkV1cm9wZSIsICJBbWVyaWNhcyIsICJPY2VhbmlhIiksCiAgbnVtX2NvdW50cmllc19hY3R1YWwgPSBjKDU0LCA0OCwgNDQsIDM1LCAxNCkgICMgcmVwbGFjZSB3aXRoIGFjdHVhbCBudW1iZXJzCikKCiMgTWVyZ2UgdGhlIHR3byBkYXRhIGZyYW1lcwpjb250aW5lbnRfY291bnQgPC0gbWVyZ2UoY29udGluZW50X2NvdW50LCBhY3R1YWxfY291bnQsIGJ5ID0gImNvbnRpbmVudCIpCgojIENvbnZlcnQgdG8gbG9uZyBmb3JtYXQgZm9yIHBsb3R0aW5nCmNvbnRpbmVudF9jb3VudF9sb25nIDwtIGNvbnRpbmVudF9jb3VudCAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jb250aW5lbnQsIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJjYXRlZ29yeSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiY291bnQiKQoKIyBDcmVhdGUgYSBiYXIgcGxvdAojIFRoZW4sIGNyZWF0ZSBhIGJhciBwbG90CmdncGxvdChjb250aW5lbnRfY291bnRfbG9uZywgYWVzKHggPSBjb250aW5lbnQsIHkgPSBjb3VudCwgZmlsbCA9IGNhdGVnb3J5KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBsYWJzKHggPSAiQ29udGluZW50IiwgeSA9ICJOdW1iZXIgb2YgQ291bnRyaWVzIiwKICAgICAgIHRpdGxlID0gIkFjdHVhbCBDb3VudHJpZXMgdnMgQ291bnRyaWVzIGluIERhdGEgcGVyIENvbnRpbmVudCIpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiIiwgbGFiZWxzID0gYygiTnVtYmVyIG9mIENvdW50cmllcyBpbiBPdXIgRGF0YSIsICJBY3R1YWwgTnVtYmVyIG9mIENvdW50cmllcyIpKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYApUaGUgcHJlc2VudGVkIHBsb3RzIHJldmVhbCB0aGF0IHdlIGhhdmUgYSBsaW1pdGVkIG51bWJlciBvZiBzYW1wbGVzIGZyb20gQWZyaWNhLCB3aGljaCBzaG91bGQgYmUgdGFrZW4gaW50byBjb25zaWRlcmF0aW9uIHdoZW4gaW50ZXJwcmV0aW5nIHRoZSByZXN1bHRzIGZvciB0aGlzIHJlZ2lvbi4gQWRkaXRpb25hbGx5LCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IG91ciBkYXRhIGZvciBBc2lhIGlzIGFsc28gcmVsYXRpdmVseSBzcGFyc2UsIHdoaWNoIG1heSBpbXBhY3QgdGhlIHJvYnVzdG5lc3Mgb2YgYW55IGNvbmNsdXNpb25zIGRyYXduIGZvciB0aGlzIGNvbnRpbmVudC4KCiMjIyMgMy4yLjIuMiBCaXZhcmlhdGUgQW5hbHlzaXMKCmBgYHtyfQpjb3VudHJ5IDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSwgY29udGluZW50KSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSwgCiAgICAgICAgICAgIHN1aWNpZGVfcGVyXzEwMGsgPSAoc3VtKGFzLm51bWVyaWMoc3VpY2lkZXNfbm8pKSAvIHN1bShhcy5udW1lcmljKHBvcHVsYXRpb24pKSkgKiAxMDAwMDAsCiAgICAgICAgICAgIC5ncm91cHM9ImRyb3AiKSAlPiUKICBhcnJhbmdlKGRlc2Moc3VpY2lkZV9wZXJfMTAwaykpCgpjb3VudHJ5JGNvdW50cnkgPC0gZmFjdG9yKGNvdW50cnkkY291bnRyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHJldihjb3VudHJ5JGNvdW50cnkpKQoKZ2dwbG90KGNvdW50cnksIGFlcyh4ID0gY291bnRyeSwgeSA9IHN1aWNpZGVfcGVyXzEwMGssIGZpbGwgPSBjb250aW5lbnQpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGdsb2JhbF9hdmVyYWdlLCBsaW5ldHlwZSA9IDIsIGNvbG9yID0gImdyZXkzNSIsIHNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJHbG9iYWwgc3VpY2lkZXMgcGVyIDEwMGssIGJ5IENvdW50cnkiLAogICAgICAgeCA9ICJDb3VudHJ5IiwgCiAgICAgICB5ID0gIlN1aWNpZGVzIHBlciAxMDBrIiwgCiAgICAgICBmaWxsID0gIkNvbnRpbmVudCIpICsKICAjY29vcmRfZmxpcCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDQ1LCAyKSkgKyAKICB0aGVtZSgKICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMjUsICJjbSIpLAogIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMC41LCB2anVzdCA9IDEsIHNpemU9IDQpLAogICNheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShpbmhlcml0LmJsYW5rID0gVFJVRSkKICApIApgYGAKTGl0aHVhbmlh4oCZcyByYXRlIGhhcyBiZWVuIGhpZ2hlc3QgYnkgYSBsYXJnZSBtYXJnaW46ID4gNDQgc3VpY2lkZXMgcGVyIDEwMGsgKHBlciB5ZWFyKQoKYGBge3J9CmNvdW50cnkgPC0gZGF0YSAlPiUKICBncm91cF9ieShjb3VudHJ5KSAlPiUKICBzdW1tYXJpemUoc3VpY2lkZV9wZXJfMTAwayA9IChzdW0oYXMubnVtZXJpYyhzdWljaWRlc19ubykpIC8gc3VtKGFzLm51bWVyaWMocG9wdWxhdGlvbikpKSAqIDEwMDAwMCwKICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIikKCmNvdW50cnlkYXRhIDwtIGpvaW5Db3VudHJ5RGF0YTJNYXAoY291bnRyeSwgam9pbkNvZGUgPSAiTkFNRSIsIG5hbWVKb2luQ29sdW1uID0gImNvdW50cnkiKQoKcGFyKG1hcj1jKDAsIDAsIDIsIDApKSAjIG1hcmdpbnMKCm1hcENvdW50cnlEYXRhKGNvdW50cnlkYXRhLCAKbmFtZUNvbHVtblRvUGxvdD0ic3VpY2lkZV9wZXJfMTAwayIsIAptYXBUaXRsZT0iU3VjaWRlIHBlciAxMDBrIGFjcm9zcyB0aGUgR2xvYmUiLCAKY29sb3VyUGFsZXR0ZSA9ICJoZWF0IiwgCm9jZWFuQ29sPSJsaWdodGJsdWUiLCAKbWlzc2luZ0NvdW50cnlDb2w9ImdyZXk2NSIsIApjYXRNZXRob2QgPSAicHJldHR5IikKYGBgCmBgYHtyfQptYXBDb3VudHJ5RGF0YShjb3VudHJ5ZGF0YSwgCm5hbWVDb2x1bW5Ub1Bsb3Q9InN1aWNpZGVfcGVyXzEwMGsiLCAKbWFwVGl0bGU9IlN1aWNpZGVzIHBlciAxMDBrIGluIEV1cmFzaWEiLCAKbWFwUmVnaW9uID0gImV1cmFzaWEiLCAKY29sb3VyUGFsZXR0ZSA9ICJoZWF0IiwgCm9jZWFuQ29sPSJsaWdodGJsdWUiLCAKbWlzc2luZ0NvdW50cnlDb2w9ImdyZXk2NSIsIAphZGRMZWdlbmQgPSBGQUxTRSwgCmNhdE1ldGhvZCA9ICJwcmV0dHkiKQpgYGAKSXQncyBlc3NlbnRpYWwgdG8gYmUgYXdhcmUgb2Ygb3VyIGRhdGEncyBsaW1pdGF0aW9ucy4gU3BlY2lmaWNhbGx5LCB3ZSdyZSBtaXNzaW5nIGEgc2lnbmlmaWNhbnQgYW1vdW50IG9mIGluZm9ybWF0aW9uIGZvciBBZnJpY2EgYW5kIEFzaWEuIE9uIHRvcCBvZiB0aGF0LCBlaWdodCBjb3VudHJpZXMgd2VyZSBleGNsdWRlZCBkdWUgdG8gbGFjayBvZiBzdWZmaWNpZW50IGRhdGEuCgpUaGVyZWZvcmUsIG91ciBhbmFseXNlcywgd2hldGhlciBvbiBhIGdsb2JhbCBvciBjb250aW5lbnQgbGV2ZWwsIG1pZ2h0IG5vdCBwcm92aWRlIGEgZnVsbHkgYWNjdXJhdGUgcGljdHVyZS4gV2UncmUgZXNzZW50aWFsbHkgdHJ5aW5nIHRvIG1ha2Ugc2Vuc2Ugb2YgYSBwdXp6bGUgd2l0aCBtaXNzaW5nIHBpZWNlcy4KCkxhc3RseSwgd2hlbiBjb21wYXJpbmcgc3VpY2lkZSByYXRlcyBiZXR3ZWVuIGRpZmZlcmVudCBjb3VudHJpZXMsIGl0J3MgY3J1Y2lhbCB0byBjb25zaWRlciB0aGF0IHdoYXQgaXMgcmVjb3JkZWQgYXMgYSBzdWljaWRlIGNhbiB2YXJ5IGJ5IGNvdW50cnkuIFRoZSByZWxpYWJpbGl0eSBvZiBzdWljaWRlIHJlcG9ydGluZyBjYW4gYWxzbyBpbmZsdWVuY2Ugb3VyIHJlc3VsdHMuCgpTbywgZXZlbiB0aG91Z2ggb3VyIGFuYWx5c2lzIGNhbiBoZWxwIGlkZW50aWZ5IHNvbWUgdHJlbmRzLCB3ZSBtdXN0IGtlZXAgdGhlc2UgY2F2ZWF0cyBpbiBtaW5kIHdoZW4gaW50ZXJwcmV0aW5nIG91ciBmaW5kaW5ncy4KCkR1ZSB0byB0aGUgbGltaXRlZCBhdmFpbGFiaWxpdHkgb2YgZGF0YSBmcm9tIEFmcmljYSwgd2UgaGF2ZSBleGNsdWRlZCB0aGlzIGNvbnRpbmVudCBmcm9tIHRoZSBjdXJyZW50IGFuYWx5c2lzLgpgYGB7cn0KCmRhdGFfY29udGluZW50IDwtIGRhdGEgJT4lIGZpbHRlcihjb250aW5lbnQgIT0gIkFmcmljYSIpCgojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBzdWljaWRlIHJhdGUgcGVyIDEwMGsKY2FsY3VsYXRlX3N1aWNpZGVfcmF0ZSA8LSBmdW5jdGlvbihzdWljaWRlc19ubywgcG9wdWxhdGlvbikgewogIChzdW0oYXMubnVtZXJpYyhzdWljaWRlc19ubykpIC8gc3VtKGFzLm51bWVyaWMocG9wdWxhdGlvbikpKSAqIDEwMDAwMAp9CgojIENhbGN1bGF0aW5nIHN1aWNpZGUgcmF0ZXMgYnkgY29udGluZW50CmNvbnRpbmVudCA8LWRhdGFfY29udGluZW50ICU+JQogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lCiAgc3VtbWFyaXplKHN1aWNpZGVfcGVyXzEwMGsgPSBjYWxjdWxhdGVfc3VpY2lkZV9yYXRlKHN1aWNpZGVzX25vLCBwb3B1bGF0aW9uKSkgJT4lCiAgYXJyYW5nZShzdWljaWRlX3Blcl8xMDBrKQoKCmNvbnRpbmVudF9wbG90IDwtIGdncGxvdChjb250aW5lbnQsIGFlcyh4ID0gY29udGluZW50LCB5ID0gc3VpY2lkZV9wZXJfMTAwaywgZmlsbCA9IGNvbnRpbmVudCkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgbGFicyh0aXRsZSA9ICJTdWljaWRlIFJhdGVzIHBlciAxMDBrIGJ5IENvbnRpbmVudCIsCiAgICAgICB4ID0gIkNvbnRpbmVudCIsIAogICAgICAgeSA9ICJTdWljaWRlcyBwZXIgMTAwayIsIAogICAgICAgZmlsbCA9ICJDb250aW5lbnQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjAsIDEpLCBtaW5vcl9icmVha3MgPSBGKQpwcmludChjb250aW5lbnRfcGxvdCkKYGBgCk91ciBwcmVsaW1pbmFyeSBhbmFseXNpcyBzdWdnZXN0cyB0aGF0IEV1cm9wZSBhcHBlYXJzIHRvIGhhdmUgYSBoaWdoZXIgc3VpY2lkZSByYXRlIGNvbXBhcmVkIHRvIG90aGVyIGNvbnRpbmVudHMuIFRvIGZ1cnRoZXIgc3Vic3RhbnRpYXRlIHRoaXMgb2JzZXJ2YXRpb24sIHdlIHNob3VsZCB2YWxpZGF0ZSBpdCB3aXRoIHJpZ29yb3VzIHN0YXRpc3RpY2FsIHRlc3RzLiBBZnRlciBhbGwsIG91ciBkYXRhc2V0IGlzIG1lcmVseSBhIHNhbXBsZSBhbmQgZG9lc24ndCBuZWNlc3NhcmlseSByZXByZXNlbnQgdGhlIHdob2xlIHBvcHVsYXRpb24uCgpIZW5jZSwgd2UgbmVlZCB0byBpbnZlc3RpZ2F0ZSBpZiB0aGUgb2JzZXJ2ZWQgZGlmZmVyZW5jZXMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCgpPdXIgbnVsbCBoeXBvdGhlc2lzIChIMCkgc3RhdGVzIHRoYXQgdGhlIG1lYW4gc3VpY2lkZSByYXRlcyBhcmUgaWRlbnRpY2FsIGFjcm9zcyBhbGwgY29udGluZW50cyBvdmVyIHRoZSAzMC15ZWFyIHNwYW4uCgpPbiB0aGUgb3RoZXIgaGFuZCwgb3VyIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgKEgxKSBhc3NlcnRzIHRoYXQgYXQgbGVhc3Qgb25lIGNvbnRpbmVudCBoYXMgYSBkaXN0aW5jdCBtZWFuIHN1aWNpZGUgcmF0ZSBjb21wYXJlZCB0byB0aGUgb3RoZXJzLgoKV2Ugc2V0IG91ciBzaWduaWZpY2FuY2UgbGV2ZWwgYXQgMC4wNSBmb3IgdGhlc2UgdGVzdHMuCgpGb3IgY29tcGFyaXNvbiwgd2UnbGwgaW5pdGlhbGx5IGVtcGxveSB0aGUgQU5PVkEgdGVzdC4gSWYgb3VyIGRhdGEgZG9lcyBub3Qgc2F0aXNmeSB0aGUgYXNzdW1wdGlvbnMgcmVxdWlyZWQgZm9yIEFOT1ZBLCB3ZSdsbCByZXNvcnQgdG8gdGhlIG5vbi1wYXJhbWV0cmljIEtydXNrYWwtV2FsbGlzIHRlc3QuIAoKYGBge3J9CmNvbnRpbmVudF9hbm92YSA8LSBhb3YobG9nX3N1aWNpZGVfcmF0aW8gfiBjb250aW5lbnQsIGRhdGEgPSBkYXRhX2NvbnRpbmVudCkKCiMgUnVuIHRoZSBBTk9WQQphbm92YV9yZXN1bHQgPC0gYW5vdmEoY29udGluZW50X2Fub3ZhKQoKIyBQcmludCB0aGUgcmVzdWx0CnByaW50KGFub3ZhX3Jlc3VsdCkKYGBgCmBgYHtyfQprcnVza2FsLnRlc3QobG9nX3N1aWNpZGVfcmF0aW8gfiBjb250aW5lbnQsIGRhdGEgPSBkYXRhX2NvbnRpbmVudCkKYGBgClRoZSBwLXZhbHVlcyBkZXJpdmVkIGZyb20gYm90aCB0ZXN0cyBhcmUgc2lnbmlmaWNhbnRseSBzbWFsbCwgbGVhZGluZyB1cyB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4gVGh1cywgd2UgY2FuIGluZmVyIHRoYXQgYXQgbGVhc3Qgb25lIGNvbnRpbmVudCBoYXMgYSBkaXN0aW5jdGl2ZSBzdWljaWRlIHJhdGUuIEhvd2V2ZXIsIHRoZSBBTk9WQSB0ZXN0IGRvZXMgbm90IHByb3ZpZGUgaW5zaWdodHMgYWJvdXQgd2hpY2ggc3BlY2lmaWMgY29udGluZW50IGRpdmVyZ2VzLCBub3IgZG9lcyBpdCBpbmRpY2F0ZSB0aGUgZXh0ZW50IG9mIHRoaXMgZGlmZmVyZW5jZS4KClRvIGRlbHZlIGludG8gdGhlc2Ugc3BlY2lmaWNzLCB3ZSdsbCB1dGlsaXplIHRoZSBUdWtleSdzIEhvbmVzdCBTaWduaWZpY2FudCBEaWZmZXJlbmNlIChIU0QpIHBvc3QtaG9jIHRlc3QuIFRoaXMgdGVzdCB3aWxsIGZhY2lsaXRhdGUgdGhlIGlkZW50aWZpY2F0aW9uIG9mIGdyb3VwcyB3aXRoIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IG1lYW5zLCBwcm92aWRpbmcgYSBjb21wcmVoZW5zaXZlIHVuZGVyc3RhbmRpbmcgb2Ygb3VyIGRhdGEuCgpgYGB7cn0KVHVrZXlIU0QoY29udGluZW50X2Fub3ZhKQpgYGAKT3VyIGluaXRpYWwgaHlwb3RoZXNpcyBzdGFuZHMgY29ycmVjdCBhcyB0aGUgZGF0YSByZXZlYWxzIHRoYXQgRXVyb3BlIGV4aGliaXRzIGEgaGlnaGVyIHN1aWNpZGUgcmF0ZSBjb21wYXJlZCB0byBvdGhlciByZWdpb25zLgpOb3csIGxldCdzIGV4cGxvcmUgaWYgZGlmZmVyZW50IHJlZ2lvbnMgd2l0aGluIEV1cm9wZSBleGhpYml0IGRpc3RpbmN0IHBhdHRlcm5zIGluIHRlcm1zIG9mIHN1aWNpZGUgcmF0ZXMuCgpgYGB7cn0KIyBEZWZpbmUgYSBsaXN0IG9mIGNvdW50cmllcyBmb3IgZWFjaCByZWdpb24KZGF0YV9ldXJvcGUgPC0gZGF0YSAlPiUKICBmaWx0ZXIoY29udGluZW50ID09ICJFdXJvcGUiKQoKbm9ydGhlcm4gPC0gYygiRGVubWFyayIsICJFc3RvbmlhIiwgIkZpbmxhbmQiLCAiSWNlbGFuZCIsICJJcmVsYW5kIiwgIkxhdHZpYSIsICJMaXRodWFuaWEiLCAiTm9yd2F5IiwgIlN3ZWRlbiIsICJVbml0ZWQgS2luZ2RvbSIsIkx1eGVtYm91cmciKQpzb3V0aGVybiA8LSBjKCJHcmVlY2UiLCAiSXRhbHkiLCAiUG9ydHVnYWwiLCAiU3BhaW4iLCJNYWx0YSIpCmVhc3Rlcm4gPC0gYygiQnVsZ2FyaWEiLCAiQ3plY2ggUmVwdWJsaWMiLCAiSHVuZ2FyeSIsICJQb2xhbmQiLCAiUm9tYW5pYSIsICJSdXNzaWFuIEZlZGVyYXRpb24iLCAiU2xvdmFraWEiLCJBbGJhbmlhIiwiVWtyYWluZSIsIkJlbGFydXMiLCJNb250ZW5lZ3JvIiwiQ3JvYXRpYSIsICJTZXJiaWEiLCAiU2xvdmVuaWEiKQp3ZXN0ZXJuIDwtIGMoIkF1c3RyaWEiLCAiQmVsZ2l1bSIsICJGcmFuY2UiLCAiR2VybWFueSIsICJOZXRoZXJsYW5kcyIsICJTd2l0emVybGFuZCIpCgoKIyBBZGQgYSBuZXcgY29sdW1uICdyZWdpb24nIGJhc2VkIG9uIHRoZSBjb3VudHJ5CmRhdGFfZXVyb3BlJHJlZ2lvbiA8LSBjYXNlX3doZW4oCiAgZGF0YV9ldXJvcGUkY291bnRyeSAlaW4lIG5vcnRoZXJuIH4gIk5vcnRoZXJuIiwKICBkYXRhX2V1cm9wZSRjb3VudHJ5ICVpbiUgc291dGhlcm4gfiAiU291dGhlcm4iLAogIGRhdGFfZXVyb3BlJGNvdW50cnkgJWluJSBlYXN0ZXJuIH4gIkVhc3Rlcm4iLAogIGRhdGFfZXVyb3BlJGNvdW50cnkgJWluJSB3ZXN0ZXJuIH4gIldlc3Rlcm4iLAogIFRSVUUgfiBOQV9jaGFyYWN0ZXJfICMgZm9yIGNvdW50cmllcyBub3QgbGlzdGVkIGFib3ZlCikKCmBgYAoKCmBgYHtyfQojIEdyb3VwIHRoZSBkYXRhIGJ5IHJlZ2lvbiBhbmQgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIHN1aWNpZGUgcmF0ZSBwZXIgMTAwayBhbmQgdGhlIG51bWJlciBvZiB1bmlxdWUgY291bnRyaWVzCnJlZ2lvbl9kYXRhIDwtIGRhdGFfZXVyb3BlICU+JQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lCiAgc3VtbWFyaXNlKGF2Z19zdWljaWRlX3JhdGVfcGVyMTAwayA9IHN1bShzdWljaWRlc19ubywgbmEucm0gPSBUUlVFKSAvIHN1bShwb3B1bGF0aW9uKSAqIDFlNSwKICAgICAgICAgICAgbnVtX2NvdW50cmllcyA9IG5fZGlzdGluY3QoY291bnRyeSkpIAoKIyBDcmVhdGUgdGhlIGJhciBwbG90CmdncGxvdChyZWdpb25fZGF0YSwgYWVzKHggPSByZW9yZGVyKHJlZ2lvbiwgLWF2Z19zdWljaWRlX3JhdGVfcGVyMTAwayksIHkgPSBhdmdfc3VpY2lkZV9yYXRlX3BlcjEwMGssIGZpbGwgPSByZWdpb24pKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsKICBsYWJzKHggPSAiUmVnaW9uIiwgeSA9ICJBdmVyYWdlIHN1aWNpZGUgcmF0ZSBwZXIgMTAwayIsIAogICAgICAgdGl0bGUgPSAiQXZlcmFnZSBTdWljaWRlIFJhdGVzIHBlciAxMDBrIGZvciBSZWdpb25zIGluIEV1cm9wZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgClRoZSBwcmVzZW50ZWQgcGxvdCBpbmRpY2F0ZXMgdGhhdCB0aGUgc3VpY2lkZSByYXRpbyBpbiBFYXN0ZXJuIEV1cm9wZSBpcyBub3RhYmx5IGhpZ2hlciBjb21wYXJlZCB0byBvdGhlciByZWdpb25zLiBUaGlzIGZpbmRpbmcgYWxpZ25zIHdpdGggdGhlIG9ic2VydmF0aW9ucyBtYWRlIGluIHRoZSBwcmV2aW91cyBzZWN0aW9ucyByZWdhcmRpbmcgdGhlIGNvdW50cmllcyB3aXRoaW4gdGhpcyByZWdpb24uCgoKIyMjIyAzLjIuMi4zIE11bHRpdmFyaWF0ZSBBbmFseWlzaXMKCk91ciBnb2FsIGlzIHRvIHVuZGVyc3RhbmQgdGhlIHRlbXBvcmFsIHRyZW5kcyBpbiBzdWljaWRlIHJhdGVzIGZvciBlYWNoIGNvdW50cnkuIFJhdGhlciB0aGFuIGNyZWF0aW5nIHZpc3VhbGl6YXRpb25zIGZvciBhbGwgOTMgY291bnRyaWVzLCB3ZSBhZG9wdCBhIG1vcmUgc3RyZWFtbGluZWQgYXBwcm9hY2ggYnkgZml0dGluZyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHRoZSBkYXRhIGZvciBlYWNoIGNvdW50cnkuIFRoaXMgYWxsb3dzIHVzIHRvIGlkZW50aWZ5IHBhdHRlcm5zIG9mIGluY3JlYXNlIG9yIGRlY3JlYXNlIGluIHN1aWNpZGUgcmF0ZXMgb3ZlciB0aW1lLgoKU3BlY2lmaWNhbGx5LCB3ZSdyZSBpbnRlcmVzdGVkIGluIHRoZSAneWVhcicgY29lZmZpY2llbnQgaW4gb3VyIGxpbmVhciBtb2RlbHMuIFRoaXMgY29lZmZpY2llbnQgc2lnbmlmaWVzIHRoZSByYXRlIG9mIGNoYW5nZSBpbiBzdWljaWRlIHJhdGVzIG92ZXIgdGltZS4gVG8gY29udHJvbCBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMsIHdlIG9ubHkgY29uc2lkZXIgdGhvc2UgY291bnRyaWVzIHdpdGggYSBjb3JyZWN0ZWQgcC12YWx1ZSBsZXNzIHRoYW4gMC4wNS4KClRvIHN1bW1hcml6ZSwgd2UgYXJlIGlkZW50aWZ5aW5nIGNvdW50cmllcyB3aGVyZSB0aGVyZSdzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBsaW5lYXIgdHJlbmQgaW4gc3VpY2lkZSByYXRlcyBvdmVyIHRpbWUuIFRoZXNlIHRyZW5kcyBhcmUgdGhlbiByYW5rLW9yZGVyZWQgYmFzZWQgb24gdGhlaXIgcmF0ZSBvZiBjaGFuZ2UsIHByb3ZpZGluZyBhIGNsZWFyIHBpY3R1cmUgb2Ygd2hlcmUgc3VpY2lkZSByYXRlcyBhcmUgaW5jcmVhc2luZyBvciBkZWNyZWFzaW5nIG1vc3QgcmFwaWRseS4KCmBgYHtyfQojIENyZWF0ZSBhIHN1bW1hcnkgZGF0YSBmcmFtZSwgZ3JvdXBpbmcgYnkgY291bnRyeSBhbmQgeWVhcgpjb3VudHJ5X3llYXIgPC0gZGF0YSAlPiUKICBncm91cF9ieShjb3VudHJ5LCB5ZWFyKSAlPiUKICBzdW1tYXJpemUoc3VpY2lkZXMgPSBzdW0oc3VpY2lkZXNfbm8pLCAKICAgICAgICAgICAgcG9wdWxhdGlvbiA9IHN1bShwb3B1bGF0aW9uKSwgCiAgICAgICAgICAgIHN1aWNpZGVfcGVyXzEwMGsgPSAoc3VpY2lkZXMgLyBwb3B1bGF0aW9uKSAqIDEwMDAwMCwgCiAgICAgICAgICAgIGdkcF9wZXJfY2FwaXRhID0gbWVhbihHRFBfcGVyX2NhcGl0YSksCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpICAjIFByZXZlbnRzIHRoZSB3YXJuaW5nIGFib3V0IGdyb3VwcwoKIyBGaXQgYSBsaW5lYXIgbW9kZWwgZm9yIGVhY2ggY291bnRyeSwgdGlkeSB0aGUgb3V0cHV0LCBhbmQgZmlsdGVyIGZvciBzaWduaWZpY2FudCB0cmVuZHMKY291bnRyeV95ZWFyX3RyZW5kcyA8LSBjb3VudHJ5X3llYXIgJT4lCiAgbmVzdChkYXRhID0gLWNvdW50cnkpICU+JSAgIyBVc2UgZXhwbGljaXQgbmFtaW5nIHRvIHByZXZlbnQgd2FybmluZwogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCB+IGxtKHN1aWNpZGVfcGVyXzEwMGsgfiB5ZWFyLCBkYXRhID0gLikpLAogICAgICAgICB0aWRpZWQgPSBtYXAobW9kZWwsIGJyb29tOjp0aWR5KSkgJT4lCiAgdW5uZXN0KGNvbHMgPSBjKHRpZGllZCkpCgojIEFkanVzdCBwLXZhbHVlcyBhbmQgZmlsdGVyIGZvciBzaWduaWZpY2FudCByZXN1bHRzCmNvdW50cnlfeWVhcl9zaWdfdHJlbmRzIDwtIGNvdW50cnlfeWVhcl90cmVuZHMgJT4lCiAgZmlsdGVyKHRlcm0gPT0gInllYXIiKSAlPiUKICBtdXRhdGUocC5hZGp1c3RlZCA9IHAuYWRqdXN0KHAudmFsdWUsIG1ldGhvZCA9ICJob2xtIikpICU+JQogIGZpbHRlcihwLmFkanVzdGVkIDwgLjA1KSAlPiUKICBhcnJhbmdlKGVzdGltYXRlKQoKIyBNYWtlIGNvdW50cnkgYW4gb3JkZXJlZCBmYWN0b3IKY291bnRyeV95ZWFyX3NpZ190cmVuZHMgPC0gbXV0YXRlKGNvdW50cnlfeWVhcl9zaWdfdHJlbmRzLCBjb3VudHJ5ID0gZmFjdG9yKGNvdW50cnksIG9yZGVyZWQgPSBUUlVFLCBsZXZlbHMgPSBjb3VudHJ5KSkKYGBgCgpgYGB7cn0KZ2dwbG90KGNvdW50cnlfeWVhcl9zaWdfdHJlbmRzLCBhZXMoeD1jb3VudHJ5LCB5PWVzdGltYXRlLCBjb2wgPSBlc3RpbWF0ZSkpICsgCiAgZ2VvbV9wb2ludChzdGF0PSdpZGVudGl0eScsIHNpemUgPSAyKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sID0gImdyZXkiLCBzaXplID0gMSkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJncmVlbiIsIGhpZ2ggPSAicmVkIikgKwogIGdlb21fc2VnbWVudChhZXMoeSA9IDAsIAogICAgICAgICAgICAgICAgICAgeCA9IGNvdW50cnksIAogICAgICAgICAgICAgICAgICAgeWVuZCA9IGVzdGltYXRlLCAKICAgICAgICAgICAgICAgICAgIHhlbmQgPSBjb3VudHJ5KSwgc2l6ZSA9IDEpICsKICBsYWJzKHRpdGxlPSJDaGFuZ2UgcGVyIHllYXIgKFN1aWNpZGVzIHBlciAxMDBrKSIsIAogICAgICAgeCA9ICJDb3VudHJ5IiwgeSA9ICJDaGFuZ2UgUGVyIFllYXIgKFN1aWNpZGVzIHBlciAxMDBrKSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC0yLCAyLCAwLjIpLCBsaW1pdHMgPSBjKC0xLjUsIDEuNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0gNSkpICsKICBjb29yZF9mbGlwKCkKCmBgYApBcHByb3hpbWF0ZWx5IGhhbGYgb2YgdGhlIGNvdW50cmllcyAoNDggb3V0IG9mIDk2KSBleGhpYml0IGEgbGluZWFyIGNoYW5nZSBpbiBzdWljaWRlIHJhdGVzIGFzIHRpbWUgcHJvZ3Jlc3Nlcy4gQW1vbmcgdGhlc2UgNDggY291bnRyaWVzLCAzMiBvZiB0aGVtIChhYm91dCB0d28tdGhpcmRzKSBzaG93IGEgZGVjcmVhc2luZyB0cmVuZC4gT3ZlcmFsbCwgdGhpcyB0cmVuZCBwcmVzZW50cyBhIHBvc2l0aXZlIHBpY3R1cmUuIEhvd2V2ZXIsIGl0IGlzIHdvcnRoIG5vdGluZyB0aGF0IHRoZSBzdWljaWRlIHJhdGVzIGluIEd1eWFuYSBhbmQgS29yZWEgYXJlIGEgY2F1c2UgZm9yIGNvbmNlcm4gYXMgdGhleSBkaXNwbGF5IGNvbmNlcm5pbmcgcGF0dGVybnMuCgoKCmBgYHtyfQojIyMgTGV0cyBsb29rIGF0IHRob3NlIGNvdW50cmllcyB3aXRoIHRoZSBzdGVlcGVzdCBpbmNyZWFzaW5nIHRyZW5kcwoKdG9wMTJfaW5jcmVhc2luZyA8LSB0YWlsKGNvdW50cnlfeWVhcl9zaWdfdHJlbmRzJGNvdW50cnksIDEyKQoKY291bnRyeV95ZWFyICU+JQogIGZpbHRlcihjb3VudHJ5ICVpbiUgdG9wMTJfaW5jcmVhc2luZykgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IHN1aWNpZGVfcGVyXzEwMGssIGNvbCA9IGNvdW50cnkpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsgCiAgZmFjZXRfd3JhcCh+IGNvdW50cnkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIGxhYnModGl0bGU9IjEyIFN0ZWVwZXN0IEluY3JlYXNpbmcgVHJlbmRzIiwgCiAgICAgICBzdWJ0aXRsZT0iT2YgY291bnRyaWVzIHdpdGggc2lnbmlmaWNhbnQgdHJlbmRzIChwIDwgMC4wNSkiLCAKICAgICAgIHggPSAiWWVhciIsIAogICAgICAgeSA9ICJTdWljaWRlcyBwZXIgMTAwayIpCmBgYApUaGUgaGlzdG9yaWNhbCBkYXRhIGZvciBHdXlhbmEgcmFpc2VzIGNvbmNlcm5zIGR1ZSB0byBhIHNlZW1pbmdseSBpbXByb2JhYmxlIGp1bXAgaW4gdGhlIHN1aWNpZGUgcmF0ZS4gV2hpbGUgR3V5YW5hIGlzIGtub3duIGZvciBoYXZpbmcgaGlnaCBzdWljaWRlIHJhdGVzLCB0aGUgc3VkZGVuIGluY3JlYXNlIG9ic2VydmVkIGFwcGVhcnMgcXVlc3Rpb25hYmxlLiBJdCBpcyBwb3NzaWJsZSB0aGF0IGNoYW5nZXMgaW4gaG93IHN1aWNpZGUgY2FzZXMgd2VyZSBjbGFzc2lmaWVkIG9yIHJlcG9ydGVkIGNvdWxkIGhhdmUgaW5mbHVlbmNlZCB0aGlzIHNpZ25pZmljYW50IHN1cmdlIGluIHRoZSBkYXQKCmBgYHtyfQpjb250aW5lbnRfdGltZSA8LSBkYXRhX2NvbnRpbmVudCAlPiUKICBncm91cF9ieSh5ZWFyLCBjb250aW5lbnQpICU+JQogIHN1bW1hcml6ZShzdWljaWRlX3Blcl8xMDBrID0gKHN1bShhcy5udW1lcmljKHN1aWNpZGVzX25vKSkgLyBzdW0oYXMubnVtZXJpYyhwb3B1bGF0aW9uKSkpICogMTAwMDAwLCAuZ3JvdXBzPSJkcm9wIikKCmNvbnRpbmVudF90aW1lJGNvbnRpbmVudCA8LSBmYWN0b3IoY29udGluZW50X3RpbWUkY29udGluZW50LCBvcmRlcmVkID0gVCwgbGV2ZWxzID0gY29udGluZW50JGNvbnRpbmVudCkKCmNvbnRpbmVudF90aW1lX3Bsb3QgPC0gZ2dwbG90KGNvbnRpbmVudF90aW1lLCBhZXMoeCA9IHllYXIsIHkgPSBzdWljaWRlX3Blcl8xMDBrLCBjb2wgPSBmYWN0b3IoY29udGluZW50KSkpICsgCiAgZmFjZXRfZ3JpZChjb250aW5lbnQgfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICBnZW9tX2xpbmUoKSArIAogIGdlb21fcG9pbnQoKSArIAogIGxhYnModGl0bGUgPSAiVHJlbmRzIE92ZXIgVGltZSwgYnkgQ29udGluZW50IiwgCiAgICAgICB4ID0gIlllYXIiLCAKICAgICAgIHkgPSAiU3VpY2lkZXMgcGVyIDEwMGsiLCAKICAgICAgIGNvbG9yID0gIkNvbnRpbmVudCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk4NSwgMjAxNSwgNSksIG1pbm9yX2JyZWFrcyA9IEYpCnByaW50KGNvbnRpbmVudF90aW1lX3Bsb3QpCmBgYAoKKiBFdXJvcGUsIGhhdmluZyB0aGUgaGlnaGVzdCBzdWljaWRlIHJhdGUgb3ZlcmFsbCwgaGFzIGV4cGVyaWVuY2VkIGEgY29uc2lzdGVudCBkZWNsaW5lIG9mIGFwcHJveGltYXRlbHkgNDAlIHNpbmNlIDE5OTUuCiogQnkgMjAxNSwgRXVyb3BlJ3Mgc3VpY2lkZSByYXRlIGhhZCBjb252ZXJnZWQgd2l0aCB0aGF0IG9mIEFzaWEgYW5kIE9jZWFuaWEsIHNob3dpbmcgc2ltaWxhciBsZXZlbHMuCiogSW4gY29udHJhc3QgdG8gdGhlIGdsb2JhbCBkb3dud2FyZCB0cmVuZCwgT2NlYW5pYSBhbmQgdGhlIEFtZXJpY2FzIGRlbW9uc3RyYXRlIGFuIHVwd2FyZCB0cmFqZWN0b3J5IGluIHN1aWNpZGUgcmF0ZXMuClRoaXMgZGlzdGluY3QgcGF0dGVybiBpcyBjb25jZXJuaW5nIGFuZCBjYWxscyBmb3IgYSB0aG9yb3VnaCBpbnZlc3RpZ2F0aW9uIGludG8gdGhlIHVuZGVybHlpbmcgZmFjdG9ycyBjb250cmlidXRpbmcgdG8gdGhpcyByaXNlLCBhcyB3ZWxsIGFzIHRoZSBpbXBsZW1lbnRhdGlvbiBvZiBlZmZlY3RpdmUgaW50ZXJ2ZW50aW9ucy4KCiMjIyAzLjIuMyBUZW1wdXJhdGUKV2UgaGF2ZSB0aHJlZSB2YXJpYWJsZXM6IGF2Z190ZW1wLCBtYXhfdGVtcCwgYW5kIG1pbl90ZW1wLiBUaGVzZSB2YXJpYWJsZXMgcmVwcmVzZW50IHRoZSBhdmVyYWdlLCBtYXhpbXVtLCBhbmQgbWluaW11bSB0ZW1wZXJhdHVyZXMsIHJlc3BlY3RpdmVseSwgZm9yIGVhY2ggY291bnRyeSBpbiBlYWNoIHllYXIuCgoKIyMjIyAzLjIuMy4xIFVuaXZhcmlhdGUgQW5hbHlzaXMKCmBgYHtyfQpsaWJyYXJ5KGdyaWRFeHRyYSkKIyBGdW5jdGlvbiB0byBjcmVhdGUgYSBsaXN0IG9mIHBsb3RzIGZvciBlYWNoIHZhcmlhYmxlCmNyZWF0ZV9wbG90cyA8LSBmdW5jdGlvbih2YXJpYWJsZV9uYW1lLCBkYXRhKSB7CiAgcDEgPC0gZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcodmFyaWFibGVfbmFtZSkpICsKICAgIGdlb21fYm94cGxvdCgpIAoKICBwMiA8LSBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh2YXJpYWJsZV9uYW1lKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gIndoaXRlIikgCgogIHAzIDwtIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHZhcmlhYmxlX25hbWUpKSArCiAgICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJzdGVlbGJsdWUiKQoKICBsaXN0KHAxLCBwMiwgcDMpCn0KCiMgQ3JlYXRlIHBsb3RzIGZvciBlYWNoIHRlbXBlcmF0dXJlIHZhcmlhYmxlCmF2Z190ZW1wX3Bsb3RzIDwtIGNyZWF0ZV9wbG90cygiYXZnX3RlbXAiLCBkYXRhKQptaW5fdGVtcF9wbG90cyA8LSBjcmVhdGVfcGxvdHMoIm1pbl90ZW1wIiwgZGF0YSkKbWF4X3RlbXBfcGxvdHMgPC0gY3JlYXRlX3Bsb3RzKCJtYXhfdGVtcCIsIGRhdGEpCgojIEFycmFuZ2UgdGhlIHBsb3RzIGluIGEgZ3JpZApncmlkLmFycmFuZ2UoZ3JvYnMgPSBjKGF2Z190ZW1wX3Bsb3RzLCBtaW5fdGVtcF9wbG90cywgbWF4X3RlbXBfcGxvdHMpLCBuY29sID0gMykKCmBgYAoKCiMjIyMgMy4yLjMuMiBCaXZhcmlhdGUgQW5hbHlzaXMKCkJlZm9yZSBjYWxjdWxhdGluZyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0ZW1wZXJhdHVyZSB2YXJpYWJsZXMgYW5kIHN1aWNpZGUgcmF0aW8sIGl0IGlzIGltcG9ydGFudCB0byByZW1vdmUgZXh0cmVtZSBvdXRsaWVycyBmcm9tIHRoZSBkYXRhc2V0LgoKCmBgYHtyfQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShrbml0cikKCiMgRnVuY3Rpb24gdG8gcHJvY2VzcyBlYWNoIHRlbXBlcmF0dXJlIHZhcmlhYmxlCmNvcnJlbGF0aW9uX3Bsb3RzIDwtIGZ1bmN0aW9uKHRlbXBfdmFyLCBkYXRhKSB7CiAgCiAgIyBDYWxjdWxhdGUgYW5kIHByaW50IHRoZSBjb3JyZWxhdGlvbiBiZWZvcmUgcmVtb3Zpbmcgb3V0bGllcnMKICBjb3JyX2JlZm9yZSA8LSBjb3IoZGF0YVtbdGVtcF92YXJdXSwgZGF0YSRzdWljaWRlX3JhdGlvLCB1c2UgPSAiY29tcGxldGUub2JzIikKICAKICAjIENhbGN1bGF0ZSBxdWFydGlsZXMgYW5kIElRUgogIFExIDwtIHF1YW50aWxlKGRhdGFbW3RlbXBfdmFyXV0sIDAuMjUsIG5hLnJtID0gVFJVRSkKICBRMyA8LSBxdWFudGlsZShkYXRhW1t0ZW1wX3Zhcl1dLCAwLjc1LCBuYS5ybSA9IFRSVUUpCiAgSVFSIDwtIFEzIC0gUTEKICAKICAjIElkZW50aWZ5IG91dGxpZXJzCiAgb3V0bGllcnMgPC0gZGF0YVtbdGVtcF92YXJdXSA8IChRMSAtIDEuNSAqIElRUikgfCBkYXRhW1t0ZW1wX3Zhcl1dID4gKFEzICsgMS41ICogSVFSKQogIAogICMgUmVtb3ZlIG91dGxpZXJzCiAgZGF0YV9ub19vdXRsaWVycyA8LSBkYXRhWyFvdXRsaWVycywgXQogIAogICMgQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgY29ycmVsYXRpb24gYWZ0ZXIgcmVtb3Zpbmcgb3V0bGllcnMKICBjb3JyX2FmdGVyIDwtIGNvcihkYXRhX25vX291dGxpZXJzW1t0ZW1wX3Zhcl1dLCBkYXRhX25vX291dGxpZXJzJHN1aWNpZGVfcmF0aW8sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQogIAogICMgQ3JlYXRlIHNjYXR0ZXIgcGxvdCB3aXRoIGEgcmVncmVzc2lvbiBsaW5lCiAgcGxvdCA8LSBnZ3Bsb3QoZGF0YV9ub19vdXRsaWVycywgYWVzX3N0cmluZyh4ID0gdGVtcF92YXIsIHkgPSAic3VpY2lkZV9yYXRpbyIpKSArCiAgICBnZW9tX3BvaW50KGNvbG9yPSJza3libHVlIiwgc2l6ZT0wLjUpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBjb2xvciA9ICJwaW5rIikgKwogICAgbGFicyh4ID0gcGFzdGUoIlRlbXBlcmF0dXJlICgiLCB0ZW1wX3ZhciwgIikiLCBzZXAgPSAiIiksIHkgPSAiU3VpY2lkZSBSYXRpbyIsIAogICAgICAgICB0aXRsZSA9IHRlbXBfdmFyKSArCiAgICB0aGVtZV9taW5pbWFsKCkKICAKICByZXR1cm4obGlzdChjb3JyX2JlZm9yZSA9IGNvcnJfYmVmb3JlLCBjb3JyX2FmdGVyID0gY29ycl9hZnRlciwgcGxvdCA9IHBsb3QpKQp9CgojIENhbGwgdGhlIGZ1bmN0aW9uIGZvciBlYWNoIHRlbXBlcmF0dXJlIHZhcmlhYmxlCnJlc3VsdF9hdmdfdGVtcCA8LSBjb3JyZWxhdGlvbl9wbG90cygiYXZnX3RlbXAiLCBkYXRhKQpyZXN1bHRfbWluX3RlbXAgPC0gY29ycmVsYXRpb25fcGxvdHMoIm1pbl90ZW1wIiwgZGF0YSkKcmVzdWx0X21heF90ZW1wIDwtIGNvcnJlbGF0aW9uX3Bsb3RzKCJtYXhfdGVtcCIsIGRhdGEpCgojIENvbWJpbmUgdGhlIHBsb3RzIGludG8gYSBncmlkCmdyaWQuYXJyYW5nZShyZXN1bHRfYXZnX3RlbXAkcGxvdCwgcmVzdWx0X21pbl90ZW1wJHBsb3QsIHJlc3VsdF9tYXhfdGVtcCRwbG90LCBucm93PTEsIG5jb2w9MykKCmBgYAoKYGBge3J9CiMgQ29tYmluZSB0aGUgY29ycmVsYXRpb24gcmVzdWx0cyBpbnRvIGEgZGF0YSBmcmFtZSBhbmQgZGlzcGxheSBhcyBhIHRhYmxlCmNvcnJlbGF0aW9uX3Jlc3VsdHMgPC0gZGF0YS5mcmFtZSgKICBUZW1wZXJhdHVyZV9WYXJpYWJsZSA9IGMoImF2Z190ZW1wIiwgIm1pbl90ZW1wIiwgIm1heF90ZW1wIiksCiAgQ29ycmVsYXRpb25fQmVmb3JlID0gYyhyZXN1bHRfYXZnX3RlbXAkY29ycl9iZWZvcmUsIHJlc3VsdF9taW5fdGVtcCRjb3JyX2JlZm9yZSwgcmVzdWx0X21heF90ZW1wJGNvcnJfYmVmb3JlKSwKICBDb3JyZWxhdGlvbl9BZnRlciA9IGMocmVzdWx0X2F2Z190ZW1wJGNvcnJfYWZ0ZXIsIHJlc3VsdF9taW5fdGVtcCRjb3JyX2FmdGVyLCByZXN1bHRfbWF4X3RlbXAkY29ycl9hZnRlcikKKQoKcHJpbnQoY29ycmVsYXRpb25fcmVzdWx0cykKYGBgCmBgYHtyfQpjb3IoZGF0YSRtaW5fdGVtcCxkYXRhJHNxcnRfc3VpY2lkZV9yYXRpbykKYGBgCgpgYGB7cn0KY29yKGRhdGEkbWluX3RlbXAsZGF0YSRsb2dfc3VpY2lkZV9yYXRpbykKYGBgCkl0IGFwcGVhcnMgdGhhdCB0aGUgdGVtcGVyYXR1cmUgdmFyaWFibGVzIGV4aGliaXQgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgc3VpY2lkZSByYXRpbyAoc3FydF9zdWljaWRlX3JhdGlvKS4gVGhpcyBvYnNlcnZhdGlvbiBpcyBub3Rld29ydGh5IGFuZCBzaG91bGQgYmUgdGFrZW4gaW50byBhY2NvdW50IGR1cmluZyBmdXJ0aGVyIGFuYWx5c2lzLgoKCk5vdywgc2ltaWxhciB0byB0aGUgcG9wdWxhdGlvbiB2YXJpYWJsZSwgbGV0J3MgYmluIHRoZSB0ZW1wZXJhdHVyZSB2YXJpYWJsZXMuIEl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgd2hlbiB1c2luZyB0aGUgSmVua3MgbWV0aG9kLCB3ZSBjbGFzc2lmeSB0aGUgdGVtcGVyYXR1cmVzIGZvciBlYWNoIHllYXIgc2VwYXJhdGVseS4gVGhpcyBhcHByb2FjaCBoZWxwcyByZWR1Y2UgdGhlIGluZmx1ZW5jZSBvZiB0ZW1wZXJhdHVyZSB2YXJpYXRpb25zIG92ZXIgdGhlIHllYXJzIGFuZCBwcm92aWRlcyBhIG1vcmUgYWNjdXJhdGUgYXNzZXNzbWVudCBvZiB0aGUgdGVtcGVyYXR1cmUgY2F0ZWdvcmllcy4KCgpgYGB7cn0KdGVtcF9kYXRhIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhciwgY291bnRyeSkgJT4lCiAgc3VtbWFyaXplKHRlbXAgPSBtZWFuKGF2Z190ZW1wKSwgLmdyb3VwcyA9ICJkcm9wIikKCmBgYAoKYGBge3J9CmZpc2hlcl9qZW5rcyA8LSBmdW5jdGlvbih4KSB7CiAgYmlucyA8LSBjbGFzc0ludGVydmFscyh4LCBuID0gNSwgc3R5bGUgPSAiZmlzaGVyIikkYnJrcwogIGN1dCh4LCBicmVha3MgPSBiaW5zLCBsYWJlbHMgPSBjKCJGcmVlemluZyIsIlZlcnlfQ29sZCIsICJDb2xkIiwgIldhcm0iLCJIb3QiICksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkKfQoKIyBBZGQgdGhlIG5ldyBjb2x1bW4gdG8gdGhlIGRhdGEgZnJhbWUKdGVtcF9kYXRhIDwtIHRlbXBfZGF0YSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBtdXRhdGUoYXZnX3RlbXBfYmluZV9qZW5rcyA9IGZpc2hlcl9qZW5rcyh0ZW1wKSkKCiMgVmlldyB0aGUgbmV3IGRhdGEKaGVhZCh0ZW1wX2RhdGEpCmBgYAoKYGBge3J9CnRlbXBfZGF0YQpgYGAKCmBgYHtyfQp0YWJsZSh0ZW1wX2RhdGEkYXZnX3RlbXBfYmluZV9qZW5rcykKYGBgCgoKQ29uc2lkZXJpbmcgdGhlIGxpbWl0ZWQgbnVtYmVyIG9mIGNvdW50cmllcyBmYWxsaW5nIHVuZGVyIHRoZSAiRnJlZXppbmciIHRlbXBlcmF0dXJlIGNhdGVnb3J5LCBpdCB3b3VsZCBiZSBhcHByb3ByaWF0ZSB0byBjb21iaW5lIHRoZSAiVmVyeSBDb2xkIiBhbmQgIkZyZWV6aW5nIiBjYXRlZ29yaWVzIGludG8gYSBzaW5nbGUgY2F0ZWdvcnkuIFRoaXMgY29uc29saWRhdGlvbiBlbnN1cmVzIHRoYXQgdGhlIHRlbXBlcmF0dXJlIGNsYXNzaWZpY2F0aW9uIHJlbWFpbnMgbWVhbmluZ2Z1bCBhbmQgcmVwcmVzZW50YXRpdmUgZGVzcGl0ZSB0aGUgc2NhcmNpdHkgb2YgZGF0YSBwb2ludHMgaW4gdGhlICJGcmVlemluZyIgY2F0ZWdvcnkKYGBge3J9CnRlbXBfZGF0YSA8LSB0ZW1wX2RhdGEgJT4lCiAgbXV0YXRlKGF2Z190ZW1wX2JpbmVfamVua3MgPSBpZmVsc2UoYXZnX3RlbXBfYmluZV9qZW5rcyA9PSAiRnJlZXppbmciLCAiVmVyeV9Db2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoYXZnX3RlbXBfYmluZV9qZW5rcykpKQpgYGAKCmBgYHtyfQp0YWJsZSh0ZW1wX2RhdGEkYXZnX3RlbXBfYmluZV9qZW5rcykKYGBgCgpgYGB7cn0KIyBsZXRzIGpvaW4gdGhpcyBkYXRhc2V0IHRvIG9yaWdpbmFsIGRhdGFzZXQgCmRhdGE8LSBkYXRhICU+JQogIGxlZnRfam9pbih0ZW1wX2RhdGElPiUKICBzZWxlY3QoY291bnRyeSx5ZWFyLGF2Z190ZW1wX2JpbmVfamVua3MpLGJ5ID0gYygieWVhciIsICJjb3VudHJ5IikpCmBgYAoKTm93LCBsZXQncyBjb21wYXJlIHRoZSBzdWljaWRlIHJhdGlvIGluIGVhY2ggY2xpbWF0ZSBjYXRlZ29yeS4KCmBgYHtyfQpkYXRhX2dyb3VwZWQgPC0gZGF0YSAlPiUKICBncm91cF9ieShhdmdfdGVtcF9iaW5lX2plbmtzKSAlPiUKICBzdW1tYXJpc2UoCiAgc3VpY2lkZV9yYXRpbyA9IG1lYW4oc3VpY2lkZV9yYXRpbykgKQogIApnZ3Bsb3QoZGF0YV9ncm91cGVkLCBhZXMoeCA9IGF2Z190ZW1wX2JpbmVfamVua3MsIHkgPSBzdWljaWRlX3JhdGlvLCBmaWxsID0gYXZnX3RlbXBfYmluZV9qZW5rcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh4ID0gIkF2ZXJhZ2UgdGVtcCBCaW4iLCB5ID0gIlN1aWNpZGUgUmF0aW8iLCB0aXRsZSA9ICJTdWljaWRlIFJhdGlvIGZvciBlYWNoIENsaW1hdGUgQ2F0ZWdvcnkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikKYGBgCkZyb20gdGhlIHBsb3RzLCBpdCBpcyBldmlkZW50IHRoYXQgY291bnRyaWVzIHdpdGggaG90IGFuZCB3YXJtIGNsaW1hdGVzIGV4aGliaXQgc2lnbmlmaWNhbnRseSBsb3dlciBzdWljaWRlIHJhdGVzLiBNb3Jlb3ZlciwgdGhlIHBsb3Qgc3VnZ2VzdHMgdGhhdCB3ZSBjYW4gc2ltcGxpZnkgdGhlIGNsaW1hdGUgY2F0ZWdvcmllcyBpbnRvIGp1c3QgdHdvIGdyb3VwczogIldhcm0iIGFuZCAiTm90IFdhcm0iIHNpbmNlIHRoZSBzdWljaWRlIHJhdGVzIGluIHRoZXNlIHR3byBjYXRlZ29yaWVzIGFyZSBzaW1pbGFyLgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZShhdmdfdGVtcF9iaW5lX2plbmtzID0gaWZlbHNlKGF2Z190ZW1wX2JpbmVfamVua3MgPT0gIkhvdCIsICJXYXJtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoYXZnX3RlbXBfYmluZV9qZW5rcykpKQpgYGAKCmBgYHtyfQpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKGF2Z190ZW1wX2JpbmVfamVua3MgPSBpZmVsc2UoYXZnX3RlbXBfYmluZV9qZW5rcyA9PSAiVmVyeV9Db2xkIiwgIkNvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihhdmdfdGVtcF9iaW5lX2plbmtzKSkpCmBgYAoKV2UgY2FuIGFwcGx5IHRoZSBzYW1lIHByb2NlZHVyZSBvZiBhbmFseXNpcyB0byB0aGUgdmFyaWFibGVzIG9mIG1heGltdW0gdGVtcGVyYXR1cmUgYW5kIG1lYW4gdGVtcGVyYXR1cmUgYXMgd2VsbC4KCmBgYHtyfQojIE1JTiBURU1QCnRlbXBfZGF0YSA8LSBkYXRhICU+JQogIGdyb3VwX2J5KHllYXIsIGNvdW50cnkpICU+JQogIHN1bW1hcml6ZSh0ZW1wID0gbWVhbihtaW5fdGVtcCksIC5ncm91cHMgPSAiZHJvcCIpCgpmaXNoZXJfamVua3MgPC0gZnVuY3Rpb24oeCkgewogIGJpbnMgPC0gY2xhc3NJbnRlcnZhbHMoeCwgbiA9IDUsIHN0eWxlID0gImZpc2hlciIpJGJya3MKICBjdXQoeCwgYnJlYWtzID0gYmlucywgbGFiZWxzID0gYygiRnJlZXppbmciLCJWZXJ5X0NvbGQiLCAiQ29sZCIsICJXYXJtIiwiSG90IiApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpCn0KCiMgQWRkIHRoZSBuZXcgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lCnRlbXBfZGF0YSA8LSB0ZW1wX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKG1pbl90ZW1wX2JpbmVfamVua3MgPSBmaXNoZXJfamVua3ModGVtcCkpCgojIHNpbmNlIHdlIGhhdmUgdmVyeSBsb3cgY291bnRyeSBmb3IgdmVyeV9jb2xkIHdlIHVuaWZ5IHZlcnkgY29sZCBhbmQgY29sZCBhbHNvIAp0ZW1wX2RhdGEgPC0gdGVtcF9kYXRhICU+JQogIG11dGF0ZShtaW5fdGVtcF9iaW5lX2plbmtzID0gaWZlbHNlKG1pbl90ZW1wX2JpbmVfamVua3MgPT0gIkZyZWV6aW5nIiwgIlZlcnlfQ29sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKG1pbl90ZW1wX2JpbmVfamVua3MpKSkKCiMgSm9pbiB0byB0aGUgZGF0YQpkYXRhPC0gZGF0YSAlPiUKICBsZWZ0X2pvaW4odGVtcF9kYXRhJT4lCiAgc2VsZWN0KGNvdW50cnkseWVhcixtaW5fdGVtcF9iaW5lX2plbmtzKSxieSA9IGMoInllYXIiLCAiY291bnRyeSIpKQoKYGBgCgpgYGB7cn0KZGF0YV9ncm91cGVkIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkobWluX3RlbXBfYmluZV9qZW5rcykgJT4lCiAgc3VtbWFyaXNlKAogIHN1aWNpZGVfcmF0aW8gPSBtZWFuKHN1aWNpZGVfcmF0aW8pICkKCmdncGxvdChkYXRhX2dyb3VwZWQsIGFlcyh4ID0gbWluX3RlbXBfYmluZV9qZW5rcywgeSA9IHN1aWNpZGVfcmF0aW8sIGZpbGwgPSBtaW5fdGVtcF9iaW5lX2plbmtzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKHggPSAibWluIHRlbXAgQmluIiwgeSA9ICJTdWljaWRlIFJhdGlvIiwgdGl0bGUgPSAiU3VpY2lkZSBSYXRpbyBmb3IgZWFjaCBtaW4gdGVtcCBCaW4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikKYGBgCgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZShtaW5fdGVtcF9iaW5lX2plbmtzID0gaWZlbHNlKG1pbl90ZW1wX2JpbmVfamVua3MgPT0gIldhcm0iLCAiSG90IiwgYXMuY2hhcmFjdGVyKG1pbl90ZW1wX2JpbmVfamVua3MpKSkKYGBgCgpgYGB7cn0KIyBNYXggVGVtcAoKdGVtcF9kYXRhIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhciwgY291bnRyeSkgJT4lCiAgc3VtbWFyaXplKHRlbXAgPSBtZWFuKG1heF90ZW1wKSkKCgpmaXNoZXJfamVua3MgPC0gZnVuY3Rpb24oeCkgewogIGJpbnMgPC0gY2xhc3NJbnRlcnZhbHMoeCwgbiA9IDUsIHN0eWxlID0gImZpc2hlciIpJGJya3MKICBjdXQoeCwgYnJlYWtzID0gYmlucywgbGFiZWxzID0gYygiRnJlZXppbmciLCJWZXJ5X0NvbGQiLCAiQ29sZCIsICJXYXJtIiwiSG90IiApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpCn0KCiMgQWRkIHRoZSBuZXcgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lCnRlbXBfZGF0YSA8LSB0ZW1wX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKG1heF90ZW1wX2JpbmVfamVua3MgPSBmaXNoZXJfamVua3ModGVtcCkpCgp0ZW1wX2RhdGEgPC0gdGVtcF9kYXRhICU+JQogIG11dGF0ZShtYXhfdGVtcF9iaW5lX2plbmtzID0gaWZlbHNlKG1heF90ZW1wX2JpbmVfamVua3MgPT0gIkZyZWV6aW5nIiwgIlZlcnlfQ29sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKG1heF90ZW1wX2JpbmVfamVua3MpKSkKCmRhdGE8LSBkYXRhICU+JQogIGxlZnRfam9pbih0ZW1wX2RhdGElPiUKICBzZWxlY3QoY291bnRyeSx5ZWFyLG1heF90ZW1wX2JpbmVfamVua3MpLGJ5ID0gYygieWVhciIsICJjb3VudHJ5IikpCmBgYAoKYGBge3J9CmRhdGFfZ3JvdXBlZCA8LSBkYXRhICU+JQogIGdyb3VwX2J5KG1heF90ZW1wX2JpbmVfamVua3MpICU+JQogIHN1bW1hcmlzZSgKICBzdWljaWRlX3JhdGlvID0gbWVhbihzdWljaWRlX3JhdGlvKSApCgpnZ3Bsb3QoZGF0YV9ncm91cGVkLCBhZXMoeCA9IG1heF90ZW1wX2JpbmVfamVua3MsIHkgPSBzdWljaWRlX3JhdGlvLCBmaWxsID0gbWF4X3RlbXBfYmluZV9qZW5rcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh4ID0gIm1heCB0ZW1wIEJpbiIsIHkgPSAiU3VpY2lkZSBSYXRpbyIsIHRpdGxlID0gIlN1aWNpZGUgUmF0aW8gZm9yIGVhY2ggbWF4IHRlbXAgQmluIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpCiAgCgpgYGAKCmBgYHtyfQpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKG1heF90ZW1wX2JpbmVfamVua3MgPSBpZmVsc2UobWF4X3RlbXBfYmluZV9qZW5rcyA9PSAiSG90IiwgIldhcm0iLCBhcy5jaGFyYWN0ZXIobWF4X3RlbXBfYmluZV9qZW5rcykpKQpgYGAKCmBgYHtyfQpkYXRhIDwtIGRhdGEgJT4lCiAgbXV0YXRlKG1heF90ZW1wX2JpbmVfamVua3MgPSBpZmVsc2UobWF4X3RlbXBfYmluZV9qZW5rcyA9PSAiVmVyeV9Db2xkIiwgIkNvbGQiLCBhcy5jaGFyYWN0ZXIobWF4X3RlbXBfYmluZV9qZW5rcykpKQpgYGAKCiMjIDMuMyBTb2NpYWwgYW5kIEVjb25vbWljYWwgIAoKIyMjIDMuMy4xIEdEUAoKIyMjIDMuMy4xLjEgVW5pdmFyaWF0ZSBBbmFseXNpcwoKYGBge3J9CiMgU2NhbGUgdGhlIEdEUCBhbmQgbG9nIEdEUCB2YXJpYWJsZXMKZGF0YSRzY2FsZWRfR0RQX2Zvcl95ZWFyIDwtIChkYXRhJEdEUF9mb3JfeWVhciAtIG1pbihkYXRhJEdEUF9mb3JfeWVhcikpLyhtYXgoZGF0YSRHRFBfZm9yX3llYXIpLW1pbihkYXRhJEdEUF9mb3JfeWVhcikpCmRhdGEkc2NhbGVkX2xvZ19HRFBfeWVhciA8LSAoZGF0YSRsb2dfR0RQX3llYXIgLSBtaW4oZGF0YSRsb2dfR0RQX3llYXIpKSAvIChtYXgoZGF0YSRsb2dfR0RQX3llYXIpIC0gbWluKGRhdGEkbG9nX0dEUF95ZWFyKSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZ3JpZEV4dHJhKQojIFBsb3R0aW5nIG9yaWdpbmFsIEdEUApwMSA8LSBnZ3Bsb3QoZGF0YSwgYWVzKEdEUF9mb3JfeWVhcikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xMDAwMDAwMDAwLCBmaWxsPSJza3libHVlIiwgY29sb3I9ImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIEdEUF9mb3JfeWVhciIsCiAgICAgICB4ID0gIkdEUF9mb3JfeWVhciIsCiAgICAgICB5ID0gIkNvdW50IikKCnAyIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9ICIiLCB5ID0gR0RQX2Zvcl95ZWFyKSkgKwogIGdlb21fYm94cGxvdChmaWxsPSJsaWdodGdyZWVuIiwgY29sb3I9ImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiQm94IFBsb3Qgb2YgR0RQX2Zvcl95ZWFyIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiR0RQX2Zvcl95ZWFyIikKCiMgUGxvdHRpbmcgc2NhbGVkIEdEUApwMyA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHNjYWxlZF9HRFBfZm9yX3llYXIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC4wMSwgZmlsbD0ic2t5Ymx1ZSIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBzY2FsZWRfR0RQX2Zvcl95ZWFyIiwKICAgICAgIHggPSAic2NhbGVkX0dEUF9mb3JfeWVhciIsCiAgICAgICB5ID0gIkNvdW50IikKCnA0IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9ICIiLCB5ID0gc2NhbGVkX0dEUF9mb3JfeWVhcikpICsKICBnZW9tX2JveHBsb3QoZmlsbD0ibGlnaHRncmVlbiIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkJveCBQbG90IG9mIHNjYWxlZF9HRFBfZm9yX3llYXIiLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICJzY2FsZWRfR0RQX2Zvcl95ZWFyIikKCiMgUGxvdHRpbmcgc2NhbGVkIGxvZyBHRFAKcDUgPC0gZ2dwbG90KGRhdGEsIGFlcyhzY2FsZWRfbG9nX0dEUF95ZWFyKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuMDEsIGZpbGw9InNreWJsdWUiLCBjb2xvcj0iYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gb2Ygc2NhbGVkX2xvZ19HRFBfeWVhciIsCiAgICAgICB4ID0gInNjYWxlZF9sb2dfR0RQX3llYXIiLAogICAgICAgeSA9ICJDb3VudCIpCgpwNiA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgeSA9IHNjYWxlZF9sb2dfR0RQX3llYXIpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGw9ImxpZ2h0Z3JlZW4iLCBjb2xvcj0iYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBzY2FsZWRfbG9nX0dEUF95ZWFyIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAic2NhbGVkX2xvZ19HRFBfeWVhciIpCgojIEFycmFuZ2UgdGhlIHBsb3RzIGluIGEgZ3JpZApncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIHA1LCBwNiwgbmNvbCA9IDIpCgpgYGAKCgpgYGB7cn0KIyBDb21wdXRpbmcgc2NhbGVkIGFuZCBsb2ctc2NhbGVkIEdEUCBwZXIgY2FwaXRhCmRhdGEkc2NhbGVkX0dEUF9wZXJfY2FwaXRhIDwtIChkYXRhJEdEUF9wZXJfY2FwaXRhIC0gbWluKGRhdGEkR0RQX3Blcl9jYXBpdGEpKSAvIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobWF4KGRhdGEkR0RQX3Blcl9jYXBpdGEpIC0gbWluKGRhdGEkR0RQX3Blcl9jYXBpdGEpKQoKZGF0YSRzY2FsZWRfbG9nX0dEUF9jYXBpdGEgPC0gKGRhdGEkbG9nX0dEUF9jYXBpdGEtbWluKGRhdGEkbG9nX0dEUF9jYXBpdGEpKS8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG1heChkYXRhJGxvZ19HRFBfY2FwaXRhKS1taW4oZGF0YSRsb2dfR0RQX2NhcGl0YSkpIApgYGAKCgpgYGB7cn0KIyBMb2FkaW5nIHJlcXVpcmVkIGxpYnJhcnkKbGlicmFyeShncmlkRXh0cmEpCgojIE9yaWdpbmFsIEdEUCBwZXIgQ2FwaXRhCnAxIDwtIGdncGxvdChkYXRhLCBhZXMoR0RQX3Blcl9jYXBpdGEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MTAwMCwgZmlsbD0ic2t5Ymx1ZSIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBHRFBfcGVyX0NhcGl0YSIsCiAgICAgICB4ID0gIkdEUF9wZXJfQ2FwaXRhIiwKICAgICAgIHkgPSAiQ291bnQiKQoKcDIgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gIiIsIHkgPSBHRFBfcGVyX2NhcGl0YSkpICsKICBnZW9tX2JveHBsb3QoZmlsbD0ibGlnaHRncmVlbiIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkJveCBQbG90IG9mIEdEUF9wZXJfQ2FwaXRhIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiR0RQX3Blcl9DYXBpdGEiKQoKIyBTY2FsZWQgR0RQIHBlciBDYXBpdGEKcDMgPC0gZ2dwbG90KGRhdGEsIGFlcyhzY2FsZWRfR0RQX3Blcl9jYXBpdGEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC4wMSwgZmlsbD0ic2t5Ymx1ZSIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBTY2FsZWQgR0RQX3Blcl9DYXBpdGEiLAogICAgICAgeCA9ICJTY2FsZWQgR0RQX3Blcl9DYXBpdGEiLAogICAgICAgeSA9ICJDb3VudCIpCgpwNCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgeSA9IHNjYWxlZF9HRFBfcGVyX2NhcGl0YSkpICsKICBnZW9tX2JveHBsb3QoZmlsbD0ibGlnaHRncmVlbiIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkJveCBQbG90IG9mIFNjYWxlZCBHRFBfcGVyX0NhcGl0YSIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIlNjYWxlZCBHRFBfcGVyX0NhcGl0YSIpCgojIFNjYWxlZCBMb2cgR0RQIHBlciBDYXBpdGEKcDUgPC0gZ2dwbG90KGRhdGEsIGFlcyhzY2FsZWRfbG9nX0dEUF9jYXBpdGEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC4wMSwgZmlsbD0ic2t5Ymx1ZSIsIGNvbG9yPSJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBTY2FsZWQgTG9nIEdEUF9wZXJfQ2FwaXRhIiwKICAgICAgIHggPSAiU2NhbGVkIExvZyBHRFBfcGVyX0NhcGl0YSIsCiAgICAgICB5ID0gIkNvdW50IikKCnA2IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9ICIiLCB5ID0gc2NhbGVkX2xvZ19HRFBfY2FwaXRhKSkgKwogIGdlb21fYm94cGxvdChmaWxsPSJsaWdodGdyZWVuIiwgY29sb3I9ImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiQm94IFBsb3Qgb2YgU2NhbGVkIExvZyBHRFBfcGVyX0NhcGl0YSIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIlNjYWxlZCBMb2cgR0RQX3Blcl9DYXBpdGEiKQoKIyBBcnJhbmdlIHRoZSBwbG90cyBpbiBhIGdyaWQKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBwNSwgcDYsIG5jb2wgPSAyKQoKYGBgCgojIyMgMy4zLjEuMiBCaXZhcmlhdGUgQW5hbHlzaXMKCmBgYHtyfQojIFByZXBhcmUgdGhlIGRhdGEgaW4gbG9uZyBmb3JtYXQKbG9uZ19kYXRhIDwtIGRhdGEgJT4lIAogIHNlbGVjdChzY2FsZWRfbG9nX0dEUF95ZWFyLCBzdWljaWRlX3JhdGlvLCBsb2dfc3VpY2lkZV9yYXRpbywgc3FydF9zdWljaWRlX3JhdGlvKSAlPiUKICBnYXRoZXIoa2V5ID0gIlZhcmlhYmxlIiwgdmFsdWUgPSAiVmFsdWUiLCAtc2NhbGVkX2xvZ19HRFBfeWVhcikKCiMgQ3JlYXRlIHRoZSBwbG90CmdncGxvdChsb25nX2RhdGEsIGFlcyh4ID0gc2NhbGVkX2xvZ19HRFBfeWVhciwgeSA9IFZhbHVlKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAic2t5Ymx1ZSIsIHNpemU9MC41KSArCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgbmNvbCA9IDMpICsKICBnZ3RpdGxlKCJTY2F0dGVycGxvdHMgb2Ygc2NhbGVkX2xvZ19HRFBfeWVhciB2cy4gZGlmZmVyZW50IHZhcmlhYmxlcyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHlsYWIoIiIpICsKICB4bGFiKCJzY2FsZWRfbG9nX0dEUF95ZWFyIikKCmBgYAoKYGBge3J9CmNvcihkYXRhJHNjYWxlZF9sb2dfR0RQX3llYXIsIGRhdGEkc3VpY2lkZV9yYXRpbykKY29yKGRhdGEkc2NhbGVkX2xvZ19HRFBfeWVhciwgZGF0YSRsb2dfc3VpY2lkZV9yYXRpbykKY29yKGRhdGEkc2NhbGVkX2xvZ19HRFBfeWVhciwgZGF0YSRzcXJ0X3N1aWNpZGVfcmF0aW8pCmBgYAoKYGBge3J9CiMgUHJlcGFyZSB0aGUgZGF0YSBpbiBsb25nIGZvcm1hdApsb25nX2RhdGEgPC0gZGF0YSAlPiUgCiAgc2VsZWN0KHNjYWxlZF9sb2dfR0RQX2NhcGl0YSwgc3VpY2lkZV9yYXRpbywgbG9nX3N1aWNpZGVfcmF0aW8sIHNxcnRfc3VpY2lkZV9yYXRpbykgJT4lCiAgZ2F0aGVyKGtleSA9ICJWYXJpYWJsZSIsIHZhbHVlID0gIlZhbHVlIiwgLXNjYWxlZF9sb2dfR0RQX2NhcGl0YSkKCiMgQ3JlYXRlIHRoZSBwbG90CmdncGxvdChsb25nX2RhdGEsIGFlcyh4ID0gc2NhbGVkX2xvZ19HRFBfY2FwaXRhLCB5ID0gVmFsdWUpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJza3libHVlIiwgc2l6ZT0wLjUpICsKICBmYWNldF93cmFwKH5WYXJpYWJsZSwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gMykgKwogIGdndGl0bGUoIlNjYXR0ZXJwbG90cyBvZiBzY2FsZWRfbG9nX0dEUF95ZWFyIHZzLiBkaWZmZXJlbnQgdmFyaWFibGVzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgeWxhYigiIikgKwogIHhsYWIoInNjYWxlZF9sb2dfR0RQX3llYXIiKQoKYGBgCmBgYHtyfQpjb3IoZGF0YSRzY2FsZWRfbG9nX0dEUF9jYXBpdGEsIGRhdGEkc3VpY2lkZV9yYXRpbykKY29yKGRhdGEkc2NhbGVkX2xvZ19HRFBfY2FwaXRhLCBkYXRhJGxvZ19zdWljaWRlX25vKQpjb3IoZGF0YSRzY2FsZWRfbG9nX0dEUF9jYXBpdGEsIGRhdGEkc3FydF9zdWljaWRlX3JhdGlvKQpjb3IoZGF0YSRHRFBfcGVyX2NhcGl0YSwgZGF0YSRzdWljaWRlX3JhdGlvKQpjb3IoZGF0YSRHRFBfcGVyX2NhcGl0YSwgZGF0YSRsb2dfc3VpY2lkZV9ubykKY29yKGRhdGEkR0RQX3Blcl9jYXBpdGEsIGRhdGEkc3FydF9zdWljaWRlX3JhdGlvKQpgYGAKSSBhbSBzdXJwcmlzZWQgdG8gZGlzY292ZXIgdGhhdCB0aGVyZSBzZWVtcyB0byBiZSBubyBldmlkZW50IGluZmx1ZW5jZSBiZXR3ZWVuIEdEUCBhbmQgc3VpY2lkZSByYXRlcy4gTGV0J3MgZnVydGhlciBpbnZlc3RpZ2F0ZSB0aGlzIG1hdHRlci4KCkNvbnNpZGVyaW5nIHRoZSBmYWN0IHRoYXQgR0RQIHRlbmRzIHRvIGluY3JlYXNlIG92ZXIgdGhlIHllYXJzIGZvciBjb3VudHJpZXMsIGl0IGJlY29tZXMgY2xlYXIgdGhhdCBHRFAgYWxvbmUgbWF5IG5vdCBiZSBhIHJlbGlhYmxlIGluZGljYXRvciBvZiBhIGNvdW50cnkncyBvdmVyYWxsIHdlYWx0aCBvciBwcm9zcGVyaXR5LiAKClRvIGdhaW4gZGVlcGVyIGluc2lnaHRzLCB3ZSBjYW4gaW50cm9kdWNlIGEgbmV3IGNvbHVtbiBjYWxsZWQgImdwZF9wcm9fY2FwLCIgd2hpY2ggcmVwcmVzZW50cyB0aGUgc2hhcmUgb2YgZWFjaCBpbmRpdmlkdWFsIHdpdGhpbiBlYWNoIGNsdXN0ZXIgZGl2aWRlZCBieSB0aGUgc3VtIG9mIHRoZSBHRFAgdmFsdWVzIGZvciBhbGwgY291bnRyaWVzLiBUaGlzIGNhbGN1bGF0aW9uIHByb3ZpZGVzIGFuIGVzdGltYXRpb24gb2YgdGhlIHNoYXJlIG9mIGVhY2ggaW5kaXZpZHVhbCB3aXRoaW4gZWFjaCBjbHVzdGVyIHJlbGF0aXZlIHRvIHRoZSB0b3RhbCBHRFAgb2YgdGhlIHdvcmxkIGR1cmluZyB0aG9zZSByZXNwZWN0aXZlIHllYXJzLkJ5IGluY29ycG9yYXRpbmcgdGhpcyBuZXcgbWVhc3VyZSwgd2UgYWltIHRvIGNhcHR1cmUgdGhlIGF2ZXJhZ2Ugc2hhcmUgb2YgZWFjaCBpbmRpdmlkdWFsIHdpdGhpbiBlYWNoIGNsdXN0ZXIgaW4gZWFjaCB5ZWFyLCBhY2NvdW50aW5nIGZvciB0aGUgZ2xvYmFsIEdEUC4gVGhpcyBhcHByb2FjaCBhbGxvd3MgdXMgdG8gZXZhbHVhdGUgdGhlIHJlbGF0aXZlIGVjb25vbWljIHBvc2l0aW9uIG9mIGluZGl2aWR1YWxzIHdpdGhpbiB0aGVpciByZXNwZWN0aXZlIGNsdXN0ZXJzIG92ZXIgdGltZS4KCkxldCdzIGFuYWx5emUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGF2ZXJhZ2UgR0RQIHBlciBjYXBpdGEgYW5kIHRoZSB5ZWFycy4KYGBge3J9CiMgQ29tcHV0ZSBhdmVyYWdlIEdEUF9wZXJfY2FwaXRhIGZvciBlYWNoIHllYXIKZGZfeWVhcmx5IDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXNlKGF2Z19HRFBfcGVyX2NhcGl0YSA9IG1lYW4oR0RQX3Blcl9jYXBpdGEsIG5hLnJtID0gVFJVRSkpCgpjb3JyZWxhdGlvbiA8LSBjb3IoZGZfeWVhcmx5JHllYXIsIGRmX3llYXJseSRhdmdfR0RQX3Blcl9jYXBpdGEpCnByaW50KGNvcnJlbGF0aW9uKQpgYGAKClRvIGFkZHJlc3MgdGhpcyBpc3N1ZSwgd2UgY2FuIGNhdGVnb3JpemUgR0RQIHBlciBjYXBpdGEgc2ltaWxhciB0byBob3cgd2UgY2F0ZWdvcml6ZWQgdGhlIHBvcHVsYXRpb24gdmFyaWFibGUgZWFybGllci4KCmBgYHtyfQpnZHBfZGF0YSA8LSBkYXRhICU+JQogIGdyb3VwX2J5KHllYXIsIGNvdW50cnkpICU+JQogIHN1bW1hcml6ZShpbmNvbWUgPSBtZWFuKEdEUF9wZXJfY2FwaXRhLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKQpgYGAKCmBgYHtyfQpmaXNoZXJfamVua3MgPC0gZnVuY3Rpb24oeCkgewogIGJpbnMgPC0gY2xhc3NJbnRlcnZhbHMoeCwgbiA9IDQsIHN0eWxlID0gImZpc2hlciIpJGJya3MKICBjdXQoeCwgYnJlYWtzID0gYmlucywgbGFiZWxzID0gYygiVmVyeV9Mb3dfaW5jb21lIiwiTG93X2luY29tZSIsICJNZWRpdW1faW5jb21lIiwgIkhpZ2hfaW5jb21lIiApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpCn0KCiMgQWRkIHRoZSBuZXcgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lCmdkcF9kYXRhIDwtIGdkcF9kYXRhICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIG11dGF0ZShnZHBfcGVyX2NhcGl0YV9iaW5lX2plbmtzID0gZmlzaGVyX2plbmtzKGluY29tZSkpCgojIFZpZXcgdGhlIG5ldyBkYXRhCnRhYmxlKGdkcF9kYXRhJGdkcF9wZXJfY2FwaXRhX2JpbmVfamVua3MpCmBgYApgYGB7cn0KZGF0YTwtIGRhdGEgJT4lCiAgbGVmdF9qb2luKGdkcF9kYXRhJT4lCiAgc2VsZWN0KGNvdW50cnkseWVhcixnZHBfcGVyX2NhcGl0YV9iaW5lX2plbmtzKSxieSA9IGMoInllYXIiLCAiY291bnRyeSIpKQpgYGAKCmBgYHtyfQpkYXRhX3N1bSA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGNvdW50cnksIHllYXIpICU+JQogIHN1bW1hcmlzZShHRFBfcGVyX2NhcGl0YSA9IG1lYW4oR0RQX3Blcl9jYXBpdGEpLCAuZ3JvdXBzID0gImRyb3AiKQoKdGhyZXNob2xkcyA8LSBxdWFudGlsZShkYXRhX3N1bSRHRFBfcGVyX2NhcGl0YSwgcHJvYnMgPSBjKDAuMjUsIDAuNSwgMC43NSkpCgojIEFzc2lnbiBlYWNoIGNvdW50cnkteWVhciBwYWlyIHRvIGEgcG9wdWxhdGlvbiBjYXRlZ29yeQpkYXRhX2Jpbm5lZCA8LSBkYXRhX3N1bSAlPiUKICBtdXRhdGUoCiAgICBnZHBfYmluZV9tZWRpYW4gPSBjYXNlX3doZW4oCiAgICAgIEdEUF9wZXJfY2FwaXRhIDw9IHRocmVzaG9sZHNbMV0gfiAiVmVyeV9sb3dfaW5jb21lIiwKICAgICAgR0RQX3Blcl9jYXBpdGEgPiB0aHJlc2hvbGRzWzFdICYgR0RQX3Blcl9jYXBpdGEgPD0gdGhyZXNob2xkc1syXSB+ICJsb3dfaW5jb21lIiwKICAgICAgR0RQX3Blcl9jYXBpdGEgPiB0aHJlc2hvbGRzWzJdICYgR0RQX3Blcl9jYXBpdGEgPD0gdGhyZXNob2xkc1szXSB+ICJNZWRpdW1faW5jb21lIiwKICAgICAgVFJVRSB+ICJoaWdoX2luY29tZSIKICAgICkKICApCgpkYXRhIDwtIGRhdGEgJT4lCiAgbGVmdF9qb2luKGRhdGFfYmlubmVkJT4lCiAgc2VsZWN0KGNvdW50cnkseWVhcixnZHBfYmluZV9tZWRpYW4pLGJ5ID0gYygieWVhciIsICJjb3VudHJ5IikpCmBgYApCeSB1c2luZyB0aGUgR0RQIGJpbm5pbmcgbWV0aG9kIChnZHBfYmluZV9qZW5rcykgZm9yIGVhY2ggeWVhciwgd2UgbWl0aWdhdGUgdGhlIGltcGFjdCBvZiB0aGUgaW5jcmVhc2luZyBHRFAgb3ZlciB0aW1lLiBOb3csIGxldCdzIGV4YW1pbmUgdGhlIHN1aWNpZGUgcmF0aW8gd2l0aGluIGVhY2ggR0RQIGNhdGVnb3J5IHRvIGdhaW4gZnVydGhlciBpbnNpZ2h0cy4KCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgbWVhbiBzdWljaWRlX3JhdGlvIGZvciBlYWNoIGdyb3VwCm1lYW5fc3VpY2lkZV9yYXRpbyA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGdkcF9wZXJfY2FwaXRhX2JpbmVfamVua3MpICU+JQogIHN1bW1hcmlzZShtZWFuX3N1aWNpZGVfcmF0aW8gPSBtZWFuKHN1aWNpZGVfcmF0aW8pKQoKIyBQcmludCB0aGUgbWVhbl9zdWljaWRlX3JhdGlvIGRhdGFmcmFtZQpwcmludChtZWFuX3N1aWNpZGVfcmF0aW8pCmBgYAoKCmBgYHtyfQojIFBsb3QgdGhlIG1lYW5fc3VpY2lkZV9yYXRpbwpnZ3Bsb3QobWVhbl9zdWljaWRlX3JhdGlvLCBhZXMoeCA9IGdkcF9wZXJfY2FwaXRhX2JpbmVfamVua3MsIHkgPSBtZWFuX3N1aWNpZGVfcmF0aW8sIGZpbGwgPSBnZHBfcGVyX2NhcGl0YV9iaW5lX2plbmtzKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsgIyBSZW1vdmUgdGhlIGNvbG9yIGxlZ2VuZAogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsgIyBDaGFuZ2UgdGhlIGNvbG9yIHBhbGV0dGUKICBsYWJzKHRpdGxlID0gIk1lYW4gU3VpY2lkZSBSYXRpbyBieSBHRFAgR3JvdXAiLAogICAgICAgeCA9ICJHRFAgR3JvdXAgKEplbmtzIE5hdHVyYWwgQnJlYWtzKSIsCiAgICAgICB5ID0gIk1lYW4gU3VpY2lkZSBSYXRpbyIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAjIFVzZSBhIGNsZWFuIHRoZW1lCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgIyBSb3RhdGUgeC1heGlzIGxhYmVscyBmb3IgYmV0dGVyIHJlYWRhYmlsaXR5CgpgYGAKSXQgYXBwZWFycyB0aGF0IGNvdW50cmllcyB3aXRoIGxvd2VyIEdEUCBwZXIgY2FwaXRhIHRlbmQgdG8gZXhoaWJpdCBsb3dlciBzdWljaWRlIHJhdGVzLiBUaGlzIG9ic2VydmF0aW9uIHN1Z2dlc3RzIGEgcG9zc2libGUgaW52ZXJzZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZWNvbm9taWMgc3RhdHVzIG9mIGEgY291bnRyeSBhbmQgaXRzIHN1aWNpZGUgcmF0ZS4KCiMjIyAzLjMuMS4zIE11bHRpdmFyaWF0ZSBBbmFseXNpcwoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBhaW0gdG8gZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBjb3VudHJ5J3MgZWNvbm9taWMgcHJvc3Blcml0eSBhbmQgaXRzIHN1aWNpZGUgcmF0ZS4gU3BlY2lmaWNhbGx5LCB3ZSBpbnZlc3RpZ2F0ZSB0aGUgcXVlc3Rpb246ICJBcyBhIGNvdW50cnkgZ2V0cyByaWNoZXIsIGRvZXMgaXRzIHN1aWNpZGUgcmF0ZSBkZWNyZWFzZT8iCgpJdCBkZXBlbmRzIG9uIHRoZSBjb3VudHJ5IC0gZm9yIGFsbW9zdCBldmVyeSBjb3VudHJ5LCB0aGVyZSBpcyBhIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiB5ZWFyIGFuZCBnZHAgcGVyIGNhcGl0YSwgaS5lLiBhcyB0aW1lIGdvZXMgb24sIGdkcCBwZXIgY2FwaXRhIGxpbmVhcmx5IGluY3JlYXNlcy4KCmBgYHtyfQpjb3VudHJ5X3llYXJfZ2RwIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSwgeWVhcikgJT4lCiAgc3VtbWFyaXplKEdEUF9wZXJfY2FwaXRhID0gbWVhbihHRFBfcGVyX2NhcGl0YSksIC5ncm91cHMgPSAiZHJvcCIpCiAgCmNvdW50cnlfeWVhcl9nZHBfY29yciA8LSBjb3VudHJ5X3llYXJfZ2RwICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShjb3VudHJ5KSAlPiUKICBzdW1tYXJpemUoeWVhcl9nZHBfY29ycmVsYXRpb24gPSBjb3IoeWVhciwgR0RQX3Blcl9jYXBpdGEpLCAuZ3JvdXBzID0gImRyb3AiKQpgYGAKSW4gb3VyIGFuYWx5c2lzLCB3ZSBleGFtaW5lZCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJ3llYXInIGFuZCAnR0RQIHBlciBjYXBpdGEnIHdpdGhpbiBpbmRpdmlkdWFsIGNvdW50cmllcyBieSBjYWxjdWxhdGluZyB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbnMuIFRoZSByZXN1bHRzIHdlcmUgaW50cmlndWluZzogdGhlIG1lYW4gY29ycmVsYXRpb24gd2FzIDAuODc4LCBpbmRpY2F0aW5nIGEgdmVyeSBzdHJvbmcgcG9zaXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcC4gRXNzZW50aWFsbHksIHRoaXMgc3VnZ2VzdHMgdGhhdCBhbiBpbmNyZWFzZSBpbiB3ZWFsdGggcGVyIHBlcnNvbiB3aXRoaW4gYSBjb3VudHJ5IGlzIGNvcnJlbGF0ZWQgd2l0aCBhbiBpbmNyZWFzZSBpbiB0aGUgY291bnRyeSdzIHN1aWNpZGUgcmF0ZSBvdmVyIHRpbWUuCgpIb3dldmVyLCBpdCdzIGNydWNpYWwgdG8gbm90ZSB0aGF0IHRoZXNlIHRyZW5kcyBhcmUgbm90IHVuaWZvcm0gYWNyb3NzIGFsbCBjb3VudHJpZXMuIFdoaWxlIHNvbWUgY291bnRyaWVzIHNob3cgYW4gaW5jcmVhc2UgaW4gc3VpY2lkZSByYXRlcyBvdmVyIHRpbWUsIG1vc3QgYXJlIGFjdHVhbGx5IGV4cGVyaWVuY2luZyBhIGRlY3JlYXNlLgoKVGhpcyBsZWFkcyB1cyB0byBhc2sgYSBzbGlnaHRseSBkaWZmZXJlbnQgYnV0IGVxdWFsbHkgc2lnbmlmaWNhbnQgcXVlc3Rpb246IERvIHdlYWx0aGllciBjb3VudHJpZXMgaGF2ZSBoaWdoZXIgc3VpY2lkZSByYXRlcz8gVG8gZXhwbG9yZSB0aGlzLCB3ZSBjYWxjdWxhdGVkIHRoZSBtZWFuIEdEUCBwZXIgY2FwaXRhIGFjcm9zcyBhbGwgYXZhaWxhYmxlIHllYXJzIGZvciBlYWNoIGNvdW50cnksIHRoZW4gY29tcGFyZWQgdGhpcyB3aXRoIHRoZSBhdmVyYWdlIHN1aWNpZGUgcmF0ZSBvdmVyIHRoZSBzYW1lIHBlcmlvZC4gVGhpcyBhcHByb2FjaCBwcm92aWRlcyB1cyB3aXRoIGEgc2luZ2xlIGRhdGEgcG9pbnQgZm9yIGVhY2ggY291bnRyeSwgb2ZmZXJpbmcgYSBnZW5lcmFsIGltcHJlc3Npb24gb2YgYSBuYXRpb24ncyBhZmZsdWVuY2UgYW5kIGl0cyBzdWljaWRlIHJhdGUuCmBgYHtyfQpjb3VudHJ5X21lYW5fZ2RwIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSwgY29udGluZW50KSAlPiUKICBzdW1tYXJpemUoc3VpY2lkZV9wZXJfMTAwayA9IChzdW0oYXMubnVtZXJpYyhzdWljaWRlc19ubykpIC8gc3VtKGFzLm51bWVyaWMocG9wdWxhdGlvbikpKSAqIDEwMDAwMCwgCiAgICAgICAgICAgIGdkcF9wZXJfY2FwaXRhID0gbWVhbihHRFBfcGVyX2NhcGl0YSksIC5ncm91cHMgPSAiZHJvcCIpCgpnZ3Bsb3QoY291bnRyeV9tZWFuX2dkcCwgYWVzKHggPSBnZHBfcGVyX2NhcGl0YSwgeSA9IHN1aWNpZGVfcGVyXzEwMGssIGNvbCA9IGNvbnRpbmVudCkpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmRvbGxhcl9mb3JtYXQocHJlZml4PSIkIiksIGJyZWFrcyA9IHNlcSgwLCA3MDAwMCwgMTAwMDApKSArIAogIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gYmV0d2VlbiBHRFAgKHBlciBjYXBpdGEpIGFuZCBTdWljaWRlcyBwZXIgMTAwayIsIAogICAgICAgc3VidGl0bGUgPSAiUGxvdCBjb250YWluaW5nIGV2ZXJ5IGNvdW50cnkiLAogICAgICAgeCA9ICJHRFAgKHBlciBjYXBpdGEpIiwgCiAgICAgICB5ID0gIlN1aWNpZGVzIHBlciAxMDBrIiwgCiAgICAgICBjb2wgPSAiQ29udGluZW50IikgCmBgYApBIG51bWJlciBvZiBjb3VudHJpZXMgaW4gb3VyIGRhdGFzZXQgZXhoaWJpdCBoaWdoIGxldmVyYWdlIGFuZCByZXNpZHVhbHMsIHBvdGVudGlhbGx5IGluZmx1ZW5jaW5nIHRoZSBmaXQgb2Ygb3VyIHJlZ3Jlc3Npb24gbGluZS4gQSBub3RhYmxlIGV4YW1wbGUgaXMgTGl0aHVhbmlhLCB3aGljaCBpcyBzaXR1YXRlZCBpbiB0aGUgdG9wIGxlZnQgb2Ygb3VyIGdyYXBoLiBUbyBtaXRpZ2F0ZSB0aGlzIGltcGFjdCwgd2UnbGwgYXBwbHkgQ29vaydzIERpc3RhbmNlIGFzIGEgbWVhc3VyZSB0byBpZGVudGlmeSBhbmQgZXhjbHVkZSBvdXRsaWVycy4gV2Ugd2lsbCBleGNsdWRlIHRob3NlIGNvdW50cmllcyB3aXRoIGEgQ29vaydzIERpc3RhbmNlIHZhbHVlIGdyZWF0ZXIgdGhhbiA0L24sIHdoaWNoIGlzIGEgY29tbW9uIHRocmVzaG9sZC4KCkFmdGVyIGltcGxlbWVudGluZyB0aGlzIGFkanVzdG1lbnQsIHdlJ2xsIGV4YW1pbmUgdGhlIHJldmlzZWQgbW9kZWwsIG5vdyBmcmVlIG9mIG91dGxpZXJzLCB0byBiZXR0ZXIgdW5kZXJzdGFuZCBpdHMgc3RhdGlzdGljYWwgcHJvcGVydGllcy4KCmBgYHtyfQptb2RlbDEgPC0gbG0oc3VpY2lkZV9wZXJfMTAwayB+IGdkcF9wZXJfY2FwaXRhLCBkYXRhID0gY291bnRyeV9tZWFuX2dkcCkKCmdkcF9zdWljaWRlX25vX291dGxpZXJzIDwtIG1vZGVsMSAlPiUKICBhdWdtZW50KCkgJT4lCiAgYXJyYW5nZShkZXNjKC5jb29rc2QpKSAlPiUKICBmaWx0ZXIoLmNvb2tzZCA8IDQvbnJvdyguKSkgJT4lICMgcmVtb3ZlcyA1LzkzIGNvdW50cmllcwogIGlubmVyX2pvaW4oY291bnRyeV9tZWFuX2dkcCwgYnkgPSBjKCJzdWljaWRlX3Blcl8xMDBrIiwgImdkcF9wZXJfY2FwaXRhIikpICU+JQogIHNlbGVjdChjb3VudHJ5LCBjb250aW5lbnQsIGdkcF9wZXJfY2FwaXRhLCBzdWljaWRlX3Blcl8xMDBrKQoKbW9kZWwyIDwtIGxtKHN1aWNpZGVfcGVyXzEwMGsgfiBnZHBfcGVyX2NhcGl0YSwgZGF0YSA9IGdkcF9zdWljaWRlX25vX291dGxpZXJzKQoKc3VtbWFyeShtb2RlbDIpCmBgYApCYXNlZCBvbiBvdXIgYW5hbHlzaXMsIHdlIGNhbm5vdCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgd2hpY2ggc3VnZ2VzdHMgdGhhdCB0aGVyZSBpcyBubyBsaW5lYXIgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgc3VpY2lkZSByYXRlIHBlciAxMDAsMDAwIHBvcHVsYXRpb24gYW5kIEdEUCBwZXIgY2FwaXRhIGZvciBlYWNoIGNvdW50cnkuIEhvd2V2ZXIsIHdlIGFudGljaXBhdGUgdGhhdCB3aGVuIHdlIGluY29ycG9yYXRlIHRoZXNlIHZhcmlhYmxlcyB3aXRoIG90aGVyIGZhY3RvcnMsIGl0IG1heSByZXZlYWwgYSBsaW5lYXIgYXNzb2NpYXRpb24uIFdlIHdpbGwgZnVydGhlciBleHBsb3JlIHRoaXMgcmVsYXRpb25zaGlwIGluIHRoZSB1cGNvbWluZyBtb2RlbCBjaGFwdGVyLgoKCkNoZWNrIGlmIHRoZSBuZXcgZmVhdHVyZXMgbWFkZSBhbnkgcHJvYmxlbSBpbiB0aGUgZGF0YXNldC4KYGBge3J9CnVuZmFjdG9yaXplZF92YXJzIDwtIGZ1bmN0aW9uKGRmKSB7CiAgdmFyX25hbWVzIDwtIG5hbWVzKGRmKQogIHVuZmFjdG9yaXplZCA8LSB2YXJfbmFtZXNbc2FwcGx5KGRmLCBmdW5jdGlvbih4KSBpcy5jaGFyYWN0ZXIoeCkgfCBpcy5pbnRlZ2VyKHgpKV0KICByZXR1cm4odW5mYWN0b3JpemVkKQp9CgojIFRlc3RpbmcgdGhlIGZ1bmN0aW9uCnVuZmFjdG9yaXplZF92YXJzKGRhdGEpCmBgYAoKbGV0cyBmYWN0b3JpemUgdGhlbSAKCmBgYHtyfQpkYXRhJGF2Z190ZW1wX2JpbmVfamVua3MgPC0gZmFjdG9yKGRhdGEkYXZnX3RlbXBfYmluZV9qZW5rcywgCiAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgCiAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJDb2xkIiwiV2FybSIpKQoKZGF0YSRtaW5fdGVtcF9iaW5lX2plbmtzIDwtIGZhY3RvcihkYXRhJG1pbl90ZW1wX2JpbmVfamVua3MsIAogICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiVmVyeV9Db2xkIiwiQ29sZCIsIkhvdCIpKQoKZGF0YSRtYXhfdGVtcF9iaW5lX2plbmtzIDwtIGZhY3RvcihkYXRhJG1heF90ZW1wX2JpbmVfamVua3MsIAogICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiQ29sZCIsIldhcm0iKSkKCmRhdGEkZ2RwX3Blcl9jYXBpdGFfYmluZV9qZW5rcyA8LSBmYWN0b3IoZGF0YSRnZHBfcGVyX2NhcGl0YV9iaW5lX2plbmtzLCAKICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlZlcnlfTG93X2luY29tZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb3dfaW5jb21lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZWRpdW1faW5jb21lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIaWdoX2luY29tZSIpKQpkYXRhJGdkcF9iaW5lX21lZGlhbiA8LSBmYWN0b3IoZGF0YSRnZHBfYmluZV9tZWRpYW4sIAogICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiVmVyeV9sb3dfaW5jb21lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd19pbmNvbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lZGl1bV9pbmNvbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hfaW5jb21lIikpCgpgYGAKCgpgYGB7cn0KbnVsbF9wZXJjZW50YWdlIDwtIGZ1bmN0aW9uKGRmKSB7CiAgIyBDYWxjdWxhdGVzIHRoZSBwZXJjZW50YWdlIG9mIG51bGwgdmFsdWVzIGluIGVhY2ggY29sdW1uIG9mIGEgZGF0YWZyYW1lCgogICMgR2V0IHRoZSBudW1iZXIgb2YgbnVsbHMgaW4gZWFjaCBjb2x1bW4KICBudWxscyA8LSBzYXBwbHkoZGYsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCgogICMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlCiAgcGVyY2VudGFnZXMgPC0gbnVsbHMgLyBucm93KGRmKSAqIDEwMAoKICAjIFJldHVybiB0aGUgcmVzdWx0IGFzIGEgZGF0YSBmcmFtZSBmb3IgZWFzaWVyIHZpZXdpbmcKICByZXR1cm4oZGF0YS5mcmFtZShDb2x1bW4gPSBuYW1lcyhkZiksIE51bGxQZXJjZW50YWdlID0gcGVyY2VudGFnZXMpKQp9CgojIFVzYWdlOgpudWxsX3BlcmNlbnRhZ2UoZGF0YSkKCmBgYAoKIyMgMy40IERlbW9ncmFwaGljIFZhcmlhYmxlcwoKRm9yIHRoaXMgc3BlY2lmaWMgcGFydCwgd2UgaGF2ZSBkYXRhIG9uIGFnZSwgZ2VuZXJhdGlvbiwgYW5kIHNleCB2YXJpYWJsZXMuIEl0IGlzIGltcG9ydGFudCB0byBlbXBoYXNpemUgdGhhdCBvdXIgZGF0YXNldCBpcyB3ZWxsLWRpc3RyaWJ1dGVkIGFtb25nIGVhY2ggc2V4IGFuZCBhZ2UgZ3JvdXAuIEVhY2ggc2V4IGFuZCBhZ2UgYm91bmQgaXMgcmVwcmVzZW50ZWQgYnkgYSBzaW5nbGUgcm93IGluIG91ciBkYXRhc2V0LCBlbnN1cmluZyBjb21wcmVoZW5zaXZlIGNvdmVyYWdlIGFjcm9zcyBkaWZmZXJlbnQgZGVtb2dyYXBoaWMgY2F0ZWdvcmllcy4KCiMjIyAzLjMuMSBVbml2YXJpYXRlIEFuYXlseXNpcwoKR2l2ZW4gdGhhdCBvdXIgZGF0YSBpcyB3ZWxsLWRpc3RyaWJ1dGVkIGFjcm9zcyBkaWZmZXJlbnQgc2V4ZXMgYW5kIGFnZSBncm91cHMsIHdlIGNhbiBwcm9jZWVkIHRvIHZpc3VhbGl6ZSBhIGJhciBwbG90IGZvciB0aGUgZ2VuZXJhdGlvbiB2YXJpYWJsZS4gVGhpcyB3aWxsIHByb3ZpZGUgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgaG93IHRoZSBkYXRhIGlzIGRpc3RyaWJ1dGVkIGFtb25nIGRpZmZlcmVudCBnZW5lcmF0aW9ucy4KCmBgYHtyfQojIERlZmluZSBjb21tb24gdGhlbWUgZm9yIGFsbCBwbG90cwpjb21tb25fdGhlbWUgPC0gdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkKICApCgojIEJhciBwbG90IGZvciBHZW5lcmF0aW9uCmdlbmVyYXRpb25fYmFyX3Bsb3QgPC0gZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBnZW5lcmF0aW9uLCBmaWxsID0gZ2VuZXJhdGlvbikpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gIkJhciBQbG90IG9mIEdlbmVyYXRpb24iLAogICAgICAgeCA9ICJHZW5lcmF0aW9uIiwKICAgICAgIHkgPSAiQ291bnQiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIGNvbW1vbl90aGVtZQoKCiMgQXJyYW5nZSBwbG90cwpncmlkLmFycmFuZ2UoZ2VuZXJhdGlvbl9iYXJfcGxvdCwgbmNvbCA9IDEpCgpgYGAKCiMjIyAzLjMuMiBCaXZhcmlhdGUgQW5hbHlzaXMKCmBgYHtyfQojIERlZmluZSB0aGUgZnVuY3Rpb24KY3JlYXRlX3N1aWNpZGVfcmF0ZV9wbG90IDwtIGZ1bmN0aW9uKGdyb3VwX3ZhcikgewogIGRhdGEgJT4lCiAgICBncm91cF9ieSghIXN5bShncm91cF92YXIpKSAlPiUKICAgIHN1bW1hcml6ZShzdWljaWRlX3Blcl8xMDBrID0gKHN1bShzdWljaWRlc19ubykgLyBzdW0ocG9wdWxhdGlvbikpICogMTAwMDAwKSAlPiUKICAgIGdncGxvdChhZXNfc3RyaW5nKHggPSBncm91cF92YXIsIHkgPSAic3VpY2lkZV9wZXJfMTAwayIsIGZpbGwgPSBncm91cF92YXIpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIGxhYnMoCiAgICAgICN0aXRsZSA9IGdyb3VwX3ZhciwgCiAgICAgIHggPSBncm91cF92YXIsCiAgICAgIHkgPSAiIgogICAgKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMC41LCB2anVzdCA9IDEsIHNpemU9IDQpLAogICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShpbmhlcml0LmJsYW5rID0gVFJVRSkKICAgICkgKwogICAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDMwKSkgKyAKICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpCn0KCnRoZW1lX3VwZGF0ZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpCgpzZXhfcGxvdCA8LSBjcmVhdGVfc3VpY2lkZV9yYXRlX3Bsb3QoInNleCIpCmFnZV9wbG90IDwtIGNyZWF0ZV9zdWljaWRlX3JhdGVfcGxvdCgiYWdlIikKZ2VuZXJhdGlvbl9wbG90IDwtIGNyZWF0ZV9zdWljaWRlX3JhdGVfcGxvdCgiZ2VuZXJhdGlvbiIpCgojIEFycmFuZ2UgdGhlIHBsb3RzCmdyaWQuYXJyYW5nZSgKICB0b3AgPSB0ZXh0R3JvYigiR2xvYmFsIHN1aWNpZGVzIHBlciAxMDBrIiwgZ3A9Z3Bhcihmb250c2l6ZT0xNiwgZm9udGZhY2U9ImJvbGQiKSksCiAgbGVmdCA9IHRleHRHcm9iKCJTdWljaWRlcyBwZXIgMTAwayIsIHJvdD05MCwgZ3A9Z3Bhcihmb250c2l6ZT0xNiwgZm9udGZhY2U9ImJvbGQiKSksCiAgYXJyYW5nZUdyb2Ioc2V4X3Bsb3QsIGFnZV9wbG90LCBnZW5lcmF0aW9uX3Bsb3QsIG5jb2w9MykKKQoKYGBgCkJhc2VkIG9uIHRoZSBwbG90cywgd2Ugb2JzZXJ2ZSB0aGF0IHN1aWNpZGVzIGFyZSBtb3JlIHByZXZhbGVudCBhbW9uZyBtZW4gYW5kIHRoZSBhZ2UgZ3JvdXAgb2YgNzUgeWVhcnMgYW5kIG9sZGVyLiBSZWdhcmRpbmcgdGhlIGdlbmVyYXRpb24gdmFyaWFibGUsIGl0IGFwcGVhcnMgdGhhdCBzdWljaWRlcyB3ZXJlIG1vcmUgY29tbW9uIGluIHRoZSBHLkkuIEdlbmVyYXRpb24gKGFsc28ga25vd24gYXMgdGhlIFdvcmxkIFdhciBJSSBnZW5lcmF0aW9uKS4gSG93ZXZlciwgaXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBvdXIgZGF0YSBpcyBub3QgZXZlbmx5IGRpc3RyaWJ1dGVkIGFtb25nIHRoZSBkaWZmZXJlbnQgZ2VuZXJhdGlvbnMsIHdpdGggbGltaXRlZCBkYXRhIGF2YWlsYWJsZSBmb3IgdGhlIEcuSS4gR2VuZXJhdGlvbiBhbmQgTWlsbGVubmlhbHMuIFRoaXMgbmVjZXNzaXRhdGVzIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiB0byBkcmF3IG1vcmUgcmVsaWFibGUgY29uY2x1c2lvbnMuCgpUbyB2YWxpZGF0ZSB0aGUgaW5zaWdodHMgZnJvbSB0aGUgcGxvdHMgYW5kIGRldGVybWluZSB0aGVpciBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UsIHdlIHdpbGwgZW1wbG95IHN0YXRpc3RpY2FsIHRlc3RzLiBUaGVzZSB0ZXN0cyB3aWxsIGhlbHAgYXNzZXNzIGlmIHRoZSBvYnNlcnZlZCBwYXR0ZXJucyBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBvciBtZXJlbHkgZHVlIHRvIHJhbmRvbSB2YXJpYXRpb24uCgoKIyMjIyAzLjMuMi4xIFRfdGVzdCBmb3IgU2V4CgpUbyBkZXRlcm1pbmUgaWYgdGhlIGFzc3VtcHRpb24gb2YgSG9tb2dlbmVpdHkgb2YgVmFyaWFuY2UgaXMgc2F0aXNmaWVkLCB3ZSBjYW4gZW1wbG95IHRoZSBMZXZlbmUncyB0ZXN0LiBUaGlzIHN0YXRpc3RpY2FsIHRlc3QgYWxsb3dzIHVzIHRvIGFzc2VzcyBpZiB0aGUgdmFyaWFuY2VzIGFyZSBlcXVhbCBhY3Jvc3MgdGhlIGRpZmZlcmVudCBncm91cHMgdW5kZXIgY29uc2lkZXJhdGlvbi4gQnkgY29uZHVjdGluZyB0aGUgTGV2ZW5lJ3MgdGVzdCwgd2UgY2FuIGV2YWx1YXRlIGlmIHRoZSBIb21vZ2VuZWl0eSBvZiBWYXJpYW5jZSBhc3N1bXB0aW9uIGhvbGRzIHRydWUgaW4gb3VyIGRhdGEuCmBgYHtyfQpsZXZlbmVUZXN0KGxvZ19zdWljaWRlX3JhdGlvIH4gc2V4LCBkYXRhID0gZGF0YSkKYGBgCgpgYGB7cn0KdC50ZXN0KGxvZ19zdWljaWRlX3JhdGlvIH4gc2V4LCBkYXRhID0gZGF0YSwgdmFyLmVxdWFsID0gRkFMU0UpCmBgYApUaGUgdGVzdCByZXN1bHRzIGluZGljYXRlIHRoYXQgdGhlcmUgaXMgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiBtYWxlcyBhbmQgZmVtYWxlcy4gVGhpcyBmaW5kaW5nIHN1Z2dlc3RzIHRoYXQgdGhlIHN1aWNpZGUgcmF0ZXMgc2lnbmlmaWNhbnRseSB2YXJ5IGJldHdlZW4gdGhlIHR3byBnZW5kZXJzLgoKIyMjIyAzLjMuMi4yIEFOT1ZBIGZvciBBZ2UKClNpbmNlIHdlIGhhdmUgbXVsdGlwbGUgYWdlIGdyb3VwcyB0byBjb21wYXJlLCB3ZSBjYW4gZW1wbG95IHRoZSBBTk9WQSAoQW5hbHlzaXMgb2YgVmFyaWFuY2UpIHRlc3QuIFRoZSBoeXBvdGhlc2lzIGZvciB0aGUgQU5PVkEgdGVzdCBpcyBhcyBmb2xsb3dzOgoKSDA6IFRoZSBtZWFuIHN1aWNpZGUgcmF0aW8gaXMgZXF1YWwgZm9yIGFsbCBhZ2UgZ3JvdXBzLgpIMTogVGhlcmUgaXMgYXQgbGVhc3Qgb25lIGFnZSBncm91cCB3aXRoIGEgZGlmZmVyZW50IG1lYW4gc3VpY2lkZSByYXRpby4KCkJ5IGNvbmR1Y3RpbmcgdGhlIEFOT1ZBIHRlc3QsIHdlIGNhbiBkZXRlcm1pbmUgaWYgdGhlcmUgaXMgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gdGhlIG1lYW4gc3VpY2lkZSByYXRpb3MgYW1vbmcgdGhlIHZhcmlvdXMgYWdlIGdyb3Vwcy4KCmBgYHtyfQojIEZpdCB0aGUgbW9kZWwKYWdlX2Fub3ZhIDwtIGFvdihsb2dfc3VpY2lkZV9yYXRpbyB+IGFnZSwgZGF0YSA9IGRhdGEpCgojIFJ1biB0aGUgQU5PVkEKYW5vdmFfcmVzdWx0IDwtIGFub3ZhKGFnZV9hbm92YSkKCiMgUHJpbnQgdGhlIHJlc3VsdApwcmludChhbm92YV9yZXN1bHQpCmBgYApUZXN0aW5nIE5vcm1hbGl0eSBvZiBSZXNpZHVhbHMgQXNzdW1wdGlvbiBmb3IgQU5PVkEKYGBge3J9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgcmVzaWR1YWxzCnJlc2lkdWFsc19kZiA8LSBkYXRhLmZyYW1lKHJlc2lkdWFscyA9IHJlc2lkdWFscyhhZ2VfYW5vdmEpKQoKIyBDcmVhdGUgaGlzdG9ncmFtIG9mIHJlc2lkdWFscwpoaXN0X3Bsb3QgPC0gZ2dwbG90KHJlc2lkdWFsc19kZiwgYWVzKHggPSByZXNpZHVhbHMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICdzdGVlbGJsdWUnLCBjb2xvciA9ICdibGFjaycsIGJpbnMgPSAzMCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh4ID0gIlJlc2lkdWFscyIsIHkgPSAiRnJlcXVlbmN5IiwKICAgICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBSZXNpZHVhbHMiKQoKIyBDcmVhdGUgUS1RIHBsb3Qgb2YgcmVzaWR1YWxzCnFxX3Bsb3QgPC0gZ2dwbG90KHJlc2lkdWFsc19kZiwgYWVzKHNhbXBsZSA9IHJlc2lkdWFscykpICsKICBnZW9tX3FxKGNvbG9yID0gJ3N0ZWVsYmx1ZScpICsKICBnZW9tX3FxX2xpbmUoY29sb3IgPSAncmVkJykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJOb3JtYWwgUS1RIFBsb3QiLAogICAgICAgeCA9ICJUaGVvcmV0aWNhbCBRdWFudGlsZXMiLAogICAgICAgeSA9ICJTYW1wbGUgUXVhbnRpbGVzIikKCiMgQXJyYW5nZSB0aGUgcGxvdHMgc2lkZSBieSBzaWRlIHVzaW5nIHRoZSBncmlkRXh0cmEgcGFja2FnZQpsaWJyYXJ5KGdyaWRFeHRyYSkKZ3JpZC5hcnJhbmdlKGhpc3RfcGxvdCwgcXFfcGxvdCwgbmNvbCA9IDIpCmBgYAoKVGVzdGluZyBIb21vZ2VuZWl0eSBvZiBWYXJpYW5jZXMgQXNzdW1wdGlvbiBmb3IgQU5PVkEKYGBge3J9CmxldmVuZVRlc3QobG9nX3N1aWNpZGVfcmF0aW8gfiBhZ2UsIGRhdGEgPSBkYXRhKQpgYGAKYGBge3J9CmJhcnRsZXR0LnRlc3QobG9nX3N1aWNpZGVfcmF0aW8gfiBhZ2UsIGRhdGEgPSBkYXRhKQpgYGAKClNpbmNlIHRoZSBwLXZhbHVlIGZyb20gYm90aCB0ZXN0cyBpcyBzbWFsbCwgd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMsIGluZGljYXRpbmcgdGhhdCB0aGUgdmFyaWFuY2VzIGFyZSBub3QgZXF1YWwgYWNyb3NzIGRpZmZlcmVudCBncm91cHMuIEluIHRoaXMgc2NlbmFyaW8sIHVzaW5nIHRoZSBBTk9WQSB0ZXN0IG1heSBub3QgcHJvdmlkZSBhY2N1cmF0ZSByZXN1bHRzLiBBcyBhbiBhbHRlcm5hdGl2ZSwgd2UgY2FuIGVtcGxveSB0aGUgS3J1c2thbC1XYWxsaXMgdGVzdCwgd2hpY2ggaXMgYSBub24tcGFyYW1ldHJpYyB0ZXN0IHN1aXRhYmxlIGZvciBzaXR1YXRpb25zIHdoZXJlIHRoZSBhc3N1bXB0aW9uIG9mIGVxdWFsIHZhcmlhbmNlcyBpcyB2aW9sYXRlZC4KCmBgYHtyfQprcnVza2FsLnRlc3QobG9nX3N1aWNpZGVfcmF0aW8gfiBhZ2UsIGRhdGEgPSBkYXRhKQpgYGAKVGhlIHJlc3VsdCBvZiB0aGUgS3J1c2thbC1XYWxsaXMgdGVzdCBhbGlnbnMgd2l0aCB0aGF0IG9mIHRoZSBBTk9WQS4gVGhlIG9idGFpbmVkIHAtdmFsdWUgaXMgc2lnbmlmaWNhbnRseSBzbWFsbCwgaW5kaWNhdGluZyB0aGF0IHRoZXJlIGlzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIHRoZSBtZWFucyBvZiB0aGUgdGFyZ2V0IHZhcmlhYmxlIGFjcm9zcyB0aGUgbGV2ZWxzIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gSG93ZXZlciwgdGhlIEFOT1ZBIGFsb25lIGRvZXMgbm90IHByb3ZpZGUgaW5mb3JtYXRpb24gYWJvdXQgd2hpY2ggc3BlY2lmaWMgZ3JvdXBzIGhhdmUgZGlmZmVyZW50IG1lYW5zLgoKVG8gaWRlbnRpZnkgdGhlIHNwZWNpZmljIGdyb3VwcyB3aXRoIHNpZ25pZmljYW50IG1lYW4gZGlmZmVyZW5jZXMsIHdlIGNhbiBlbXBsb3kgVHVrZXkncyBIb25lc3QgU2lnbmlmaWNhbnQgRGlmZmVyZW5jZSAoSFNEKSB0ZXN0LiBUaGlzIHBvc3QtaG9jIHRlc3QgYWxsb3dzIHVzIHRvIGNvbmR1Y3QgcGFpcndpc2UgY29tcGFyaXNvbnMgYW5kIGRldGVybWluZSB3aGljaCBncm91cHMgZXhoaWJpdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIHRoZWlyIG1lYW5zLiBCeSBwZXJmb3JtaW5nIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbnMgdXNpbmcgdGhlIFR1a2V5J3MgSFNEIHRlc3QsIHdlIGNhbiBnYWluIG1vcmUgaW5zaWdodHMgaW50byB0aGUgc3BlY2lmaWMgZ3JvdXAgZGlmZmVyZW5jZXMuCmBgYHtyfQpUdWtleUhTRChhZ2VfYW5vdmEpCmBgYAoKIyMjIyAzLjMuMi4zIEFOT1ZBIGZvciBHZW5lcmF0aW9uCmBgYHtyfQojIEZpdCB0aGUgbW9kZWwKZ2VuZXJhdGlvbl9hbm92YSA8LSBhb3YobG9nX3N1aWNpZGVfcmF0aW8gfiBnZW5lcmF0aW9uLCBkYXRhID0gZGF0YSkKCiMgUnVuIHRoZSBBTk9WQQphbm92YV9yZXN1bHQgPC0gYW5vdmEoZ2VuZXJhdGlvbl9hbm92YSkKCiMgUHJpbnQgdGhlIHJlc3VsdApwcmludChhbm92YV9yZXN1bHQpCmBgYApgYGB7cn0KbGV2ZW5lVGVzdChsb2dfc3VpY2lkZV9yYXRpbyB+IGdlbmVyYXRpb24sIGRhdGEgPSBkYXRhKQpgYGAKYGBge3J9CmtydXNrYWwudGVzdChsb2dfc3VpY2lkZV9yYXRpbyB+IGdlbmVyYXRpb24sIGRhdGEgPSBkYXRhKQpgYGAKClRoZSBvYnRhaW5lZCBzbWFsbCBwLXZhbHVlcyBpbmRpY2F0ZSB0aGF0IHRoZXJlIGlzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIHRoZSBzdWljaWRlIHJhdGUgYW1vbmcgZGlmZmVyZW50IGdlbmVyYXRpb25zLiBUaGlzIGZpbmRpbmcgc3VnZ2VzdHMgdGhhdCB0aGUgc3VpY2lkZSByYXRlcyB2YXJ5IHNpZ25pZmljYW50bHkgYWNyb3NzIHRoZSBkaWZmZXJlbnQgZ2VuZXJhdGlvbmFsIGNvaG9ydHMuCgojIyMgMy4zLjMgTXVsdGl2YXJpYXRlIEFuYWx5c2lzCgojIyMjIDMuMy4zLjEgVHJlbmRzIE92ZXIgVGltZQoKIyMjIyMgMy4zLjMuMS4xIFNleApgYGB7cn0Kc2V4X3RpbWVfcGxvdCA8LSBkYXRhICU+JQogIGdyb3VwX2J5KHllYXIsIHNleCkgJT4lCiAgc3VtbWFyaXplKHN1aWNpZGVfcGVyXzEwMGsgPSAoc3VtKGFzLm51bWVyaWMoc3VpY2lkZXNfbm8pKSAvIHN1bShhcy5udW1lcmljKHBvcHVsYXRpb24pKSkgKiAxMDAwMDAsCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBzdWljaWRlX3Blcl8xMDBrLCBjb2wgPSBmYWN0b3Ioc2V4KSkpICsgCiAgZmFjZXRfZ3JpZChzZXggfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICBnZW9tX2xpbmUoKSArIAogIGdlb21fcG9pbnQoKSArIAogIGxhYnModGl0bGUgPSAiVHJlbmRzIE92ZXIgVGltZSwgYnkgU2V4IiwgCiAgICAgICB4ID0gIlllYXIiLCAKICAgICAgIHkgPSAiU3VpY2lkZXMgcGVyIDEwMGsiLCAKICAgICAgIGNvbG9yID0gIlNleCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk4NSwgMjAxNSwgNSksIG1pbm9yX2JyZWFrcyA9IEYpCgpncmlkLmFycmFuZ2Uoc2V4X3RpbWVfcGxvdCwgbmNvbCA9IDEpCmBgYApHbG9iYWxseSwgdGhlIHN1aWNpZGUgcmF0ZSBmb3IgbWVuIGhhcyBiZWVuIGFwcHJveGltYXRlbHkgMy41IHRpbWVzIGhpZ2hlciBjb21wYXJlZCB0byB3b21lbi4gQm90aCBtYWxlIGFuZCBmZW1hbGUgc3VpY2lkZSByYXRlcyByZWFjaGVkIHRoZWlyIHBlYWsgaW4gMTk5NSBhbmQgaGF2ZSBiZWVuIGRlY2xpbmluZyBzaW5jZSB0aGVuLiBJdCBpcyBub3Rld29ydGh5IHRoYXQgdGhlIHJhdGlvIG9mIG1hbGUgdG8gZmVtYWxlIHN1aWNpZGUgcmF0ZXMsIHdoaWNoIHN0YW5kcyBhdCAzLjUgOiAxLCBoYXMgcmVtYWluZWQgcmVsYXRpdmVseSBjb25zaXN0ZW50IHNpbmNlIHRoZSBtaWQtMTk5MHMuIEhvd2V2ZXIsIGl0IGlzIGltcG9ydGFudCB0byBtZW50aW9uIHRoYXQgZHVyaW5nIHRoZSAxOTgwcywgdGhpcyByYXRpbyB3YXMgY29tcGFyYXRpdmVseSBsb3dlci4KCiMjIyMjIDMuMy4zLjEuMiBBZ2UKCmBgYHtyfQphZ2VfdGltZV9wbG90IDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhciwgYWdlKSAlPiUKICBzdW1tYXJpemUoc3VpY2lkZV9wZXJfMTAwayA9IChzdW0oYXMubnVtZXJpYyhzdWljaWRlc19ubykpIC8gc3VtKGFzLm51bWVyaWMocG9wdWxhdGlvbikpKSAqIDEwMDAwMCwuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gc3VpY2lkZV9wZXJfMTAwaywgY29sID0gYWdlKSkgKyAKICBmYWNldF9ncmlkKGFnZSB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiKSArIAogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgbGFicyh0aXRsZSA9ICJUcmVuZHMgT3ZlciBUaW1lLCBieSBBZ2UiLCAKICAgICAgIHggPSAiWWVhciIsIAogICAgICAgeSA9ICJTdWljaWRlcyBwZXIgMTAwayIsIAogICAgICAgY29sb3IgPSAiQWdlIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTg1LCAyMDE1LCA1KSwgbWlub3JfYnJlYWtzID0gRkFMU0UpCgpncmlkLmFycmFuZ2UoYWdlX3RpbWVfcGxvdCwgbmNvbCA9IDEpCmBgYAoKR2xvYmFsbHksIHRoZXJlIGlzIGFuIGluY3JlYXNlZCBsaWtlbGlob29kIG9mIHN1aWNpZGUgYXMgYWdlIGFkdmFuY2VzLiBTaW5jZSAxOTk1LCB0aGUgc3VpY2lkZSByYXRlIGhhcyBiZWVuIGNvbnNpc3RlbnRseSBkZWNyZWFzaW5nIGZvciBpbmRpdmlkdWFscyBhY3Jvc3MgYWxsIGFnZSBncm91cHMuIE5vdGFibHksIHRoZSBzdWljaWRlIHJhdGUgZm9yIHRob3NlIGFnZWQgNzUgYW5kIGFib3ZlIGhhcyB3aXRuZXNzZWQgYSBzaWduaWZpY2FudCBkZWNsaW5lIG9mIG92ZXIgNTAlIHNpbmNlIDE5OTAuIFRoZXNlIHRyZW5kcyByZWZsZWN0IHBvc2l0aXZlIHByb2dyZXNzIGluIGFkZHJlc3NpbmcgYW5kIHJlZHVjaW5nIHN1aWNpZGUgcmF0ZXMgYW1vbmcgZGlmZmVyZW50IGFnZSBkZW1vZ3JhcGhpY3Mgb3ZlciB0aGUgcGFzdCBmZXcgZGVjYWRlcy4KCgojIyMjIyAzLjMuMy4xLjMgR2VuZXJhdGlvbgoKV2hlbiBkZWFsaW5nIHdpdGggY29udGludW91cyBkYXRhLCBzdWNoIGFzIHNvbWVvbmUncyBhZ2UgaW4gYSBnaXZlbiB5ZWFyLCBpdCBpcyBjb21tb25seSBhc3N1bWVkIHRoYXQgdGhlaXIgYWdlIGRldGVybWluZXMgdGhlaXIgZ2VuZXJhdGlvbi4gSG93ZXZlciwgaXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBpbiBvdXIgZGF0YXNldCwgbm90IGV2ZXJ5b25lIHdpdGhpbiB0aGUgc2FtZSBhZ2UgZ3JvdXAgaW4gYSBzcGVjaWZpYyB5ZWFyIGJlbG9uZ3MgdG8gdGhlIHNhbWUgZ2VuZXJhdGlvbi4KCmBgYHtyfQojIENyZWF0ZSB0aGUgcGxvdApkYXRhICU+JQogIGdyb3VwX2J5KGdlbmVyYXRpb24sIGFnZSwgeWVhcikgJT4lCiAgc3VtbWFyaXplKHN1aWNpZGVfcGVyXzEwMGsgPSAoc3VtKHN1aWNpZGVzX25vKSAvIHN1bShwb3B1bGF0aW9uKSkgKiAxMDAwMDAsIC5ncm91cHMgPSAnZHJvcCcpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBzdWljaWRlX3Blcl8xMDBrLCBjb2xvciA9IGdlbmVyYXRpb24pKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fbGluZSgpICsgCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsKICBmYWNldF9ncmlkKGFnZSB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk4NSwgMjAyMCwgNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKwogIGxhYnModGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gR2VuZXJhdGlvbiwgQWdlICYgWWVhciIsIAogICAgICAgeCA9ICJZZWFyIiwgCiAgICAgICB5ID0gIlN1aWNpZGVzIHBlciAxMDBrIiwgCiAgICAgICBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyAKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIKICApCgpgYGAKVW5kZXJzdGFuZGluZyB0aGUgdHJlbmQgb2YgZ2VuZXJhdGlvbiBzdWljaWRlIHJhdGVzIG92ZXIgdGltZSBiZWNvbWVzIHByb2JsZW1hdGljIGR1ZSB0byBvdmVybGFwcGluZyBhZ2UgY2F0ZWdvcmllcy4gV2hlbiBjb21wYXJpbmcgdGhlIHJhdGVzIGJlbG93IHdpdGggdGhlIHBsb3R0ZWQgZGF0YSwgd2Ugbm90aWNlIHRoYXQgbGFyZ2Ugc3Bpa2VzIG9jY3VyIHdoZW4gZGlmZmVyZW50IGFnZSBncm91cHMgYXJlIGNsYXNzaWZpZWQgYXMgcGFydCBvZiBhIGNlcnRhaW4gZ2VuZXJhdGlvbiBvciBub3QuIEZvciBpbnN0YW5jZSwgaW4gMTk5MSwgdGhlcmUgaXMgYSBzdXBwb3NlZCBzcGlrZSBpbiB0aGUgc3VpY2lkZSByYXRlIGZvciB0aGUgRy5JLiBnZW5lcmF0aW9uLiBIb3dldmVyLCB0aGlzIHNwaWtlIG9jY3VycyBiZWNhdXNlIGluZGl2aWR1YWxzIGFnZWQgJzU1IC0gNzUnIGFyZSBzdWRkZW5seSBleGNsdWRlZCBmcm9tIHRoaXMgZ2VuZXJhdGlvbiBjbGFzc2lmaWNhdGlvbi4KCmBgYHtyfQojIERlZmluZSBhIGNvbW1vbiB0aGVtZQoKY29tbW9uX3RoZW1lIDwtIAogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgI3N0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLAogICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1LCBjb2xvciA9ICJ3aGl0ZSIpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKQogICAgI3BhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTgwIiksCiAgICAjcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5OTAiKQogICkKCiMgQ3JlYXRlIHRoZSBzdWljaWRlIHJhdGUgcGxvdApnZW5lcmF0aW9uX3JhdGUgPC0gZGF0YSAlPiUKICBncm91cF9ieShnZW5lcmF0aW9uLCB5ZWFyKSAlPiUKICBzdW1tYXJpemUoc3VpY2lkZV9wZXJfMTAwayA9IChzdW0oc3VpY2lkZXNfbm8pIC8gc3VtKHBvcHVsYXRpb24pKSAqIDEwMDAwMCwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IHN1aWNpZGVfcGVyXzEwMGssIGNvbG9yID0gZ2VuZXJhdGlvbikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBhbHBoYSA9IDAuOCkgKyAKICBnZW9tX2xpbmUoYWxwaGEgPSAwLjYsIGxpbmV3aWR0aD0gMSkgKyAKICBmYWNldF9ncmlkKGdlbmVyYXRpb24gfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5ODUsIDIwMjAsIDUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKICBsYWJzKHRpdGxlID0gIlN1aWNpZGVzIHBlciAxMDBrLCBieSBHZW5lcmF0aW9uIiwgCiAgICAgICB4ID0gIlllYXIiLCAKICAgICAgIHkgPSAiU3VpY2lkZXMgcGVyIDEwMGsiKSArCiAgY29tbW9uX3RoZW1lCgojIENyZWF0ZSB0aGUgcG9wdWxhdGlvbiBwbG90CmdlbmVyYXRpb25fcG9wdWxhdGlvbiA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGdlbmVyYXRpb24sIHllYXIpICU+JQogIHN1bW1hcml6ZShwb3B1bGF0aW9uID0gc3VtKHBvcHVsYXRpb24pLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gcG9wdWxhdGlvbiAvIDEwMDAwMDAsIGNvbG9yID0gZ2VuZXJhdGlvbikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBhbHBoYSA9IDAuOCkgKyAKICBnZW9tX2xpbmUoYWxwaGEgPSAwLjYsIGxpbmV3aWR0aD0gMSkgKyAKICBmYWNldF9ncmlkKGdlbmVyYXRpb24gfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5ODUsIDIwMjAsIDUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKICBsYWJzKHRpdGxlID0gIlBvcHVsYXRpb24sIGJ5IEdlbmVyYXRpb24iLCAKICAgICAgIHggPSAiWWVhciIsIAogICAgICAgeSA9ICJQb3B1bGF0aW9uIChNaWxsaW9ucykiKSArCiAgY29tbW9uX3RoZW1lCgojIEFycmFuZ2UgdGhlIHBsb3RzCmdyaWQuYXJyYW5nZShnZW5lcmF0aW9uX3JhdGUsIGdlbmVyYXRpb25fcG9wdWxhdGlvbiwgbmNvbCA9IDIpCgpgYGAKVGhlIGlzc3VlIGF0IGhhbmQgbGlrZWx5IHN0ZW1zIGZyb20gdGhlIG1ldGhvZG9sb2d5IHVzZWQgdG8gY3JlYXRlIHRoZSBkYXRhc2V0LiBJdCBhcHBlYXJzIHRoYXQgdGhlIGdlbmVyYXRpb24gdmFyaWFibGUgd2FzIGFkZGVkIGFmdGVyIHN1bW1hcml6aW5nIHRoZSBkYXRhIGJ5IGNvdW50cnksIHllYXIsIGFnZSwgYW5kIHNleCwgd2hpY2ggaXMgcHJvYmxlbWF0aWMuIEluIHJlYWxpdHksIG5vdCBldmVyeW9uZSB3aXRoaW4gYSBzcGVjaWZpYyBhZ2UgZ3JvdXAgYW5kIHllYXIgY2FuIGJlIGFjY3VyYXRlbHkgYXNzaWduZWQgdG8gYSBzaW5nbGUgZ2VuZXJhdGlvbi4KCkFzIGEgcmVzdWx0LCB0aGUgb2JzZXJ2ZWQgInNwaWtlcyIgaW4gZ2VuZXJhdGlvbiBhY3Jvc3MgdGltZSBsYWNrIG1lYW5pbmdmdWwgaW50ZXJwcmV0YXRpb24uIENvbnNlcXVlbnRseSwgd2UgY2Fubm90IGRyYXcgYW55IGNvbmNsdXNpdmUgY29uY2x1c2lvbnMgcmVnYXJkaW5nIHRoZSBzdWljaWRlIHJhdGVzIGFtb25nIGRpZmZlcmVudCBnZW5lcmF0aW9ucyBiYXNlZCBvbiB0aGlzIGRhdGFzZXQuCgoKIyMjIyAzLjMuMy4yIEFnZSBkaWZmZXJlbmNlcywgYnkgQ29udGluZW50CgpgYGB7cn0KZ2xvYmFsX2F2ZXJhZ2UgPC0gKHN1bShhcy5udW1lcmljKGRhdGEkc3VpY2lkZXNfbm8pKSAvIHN1bShhcy5udW1lcmljKGRhdGEkcG9wdWxhdGlvbikpKSAqIDEwMDAwMAoKZGF0YSAlPiUKICBncm91cF9ieShjb250aW5lbnQsIGFnZSkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCksIAogICAgICAgICAgICBzdWljaWRlcyA9IHN1bShhcy5udW1lcmljKHN1aWNpZGVzX25vKSksIAogICAgICAgICAgICBwb3B1bGF0aW9uID0gc3VtKGFzLm51bWVyaWMocG9wdWxhdGlvbikpLCAKICAgICAgICAgICAgc3VpY2lkZV9wZXJfMTAwayA9IChzdWljaWRlcyAvIHBvcHVsYXRpb24pICogMTAwMDAwLCAuZ3JvdXBzPSAiZHJvcCIpICU+JQogIGdncGxvdChhZXMoeCA9IGNvbnRpbmVudCwgeSA9IHN1aWNpZGVfcGVyXzEwMGssIGZpbGwgPSBhZ2UpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gZ2xvYmFsX2F2ZXJhZ2UsIGxpbmV0eXBlID0gMiwgY29sb3IgPSAiZ3JleTM1Iiwgc2l6ZSA9IDEpICsKICBsYWJzKHRpdGxlID0gIkFnZSBEaXNwYXJpdHksIGJ5IENvbnRpbmVudCIsCiAgICAgICB4ID0gIkNvbnRpbmVudCIsIAogICAgICAgeSA9ICJTdWljaWRlcyBwZXIgMTAwayIsIAogICAgICAgZmlsbCA9ICJBZ2UiKSsKICBjb29yZF9mbGlwKCkKYGBgCkluIHRoZSByZWdpb25zIG9mIHRoZSBBbWVyaWNhcywgQXNpYSwgYW5kIEV1cm9wZSwgd2hpY2ggY29tcHJpc2UgdGhlIG1ham9yaXR5IG9mIHRoZSBkYXRhc2V0LCB0aGUgc3VpY2lkZSByYXRlIHRlbmRzIHRvIGluY3JlYXNlIHdpdGggYWdlLiBIb3dldmVyLCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGZvciBPY2VhbmlhIGFuZCBBZnJpY2EsIHRoZSBoaWdoZXN0IHN1aWNpZGUgcmF0ZXMgYXJlIG9ic2VydmVkIGFtb25nIGluZGl2aWR1YWxzIGFnZWQgMjUgdG8gMzQuIE5ldmVydGhlbGVzcywgZHVlIHRvIHRoZSBsaW1pdGVkIGF2YWlsYWJpbGl0eSBvZiBkYXRhIGZvciBBZnJpY2EsIHRoaXMgcGFydGljdWxhciBmaW5kaW5nIG1heSBub3QgYmUgZW50aXJlbHkgcmVsaWFibGUuIEZ1cnRoZXIgaW52ZXN0aWdhdGlvbiBhbmQgZGF0YSBjb2xsZWN0aW9uIGFyZSBuZWNlc3NhcnkgdG8gcHJvdmlkZSBtb3JlIGFjY3VyYXRlIGluc2lnaHRzIGludG8gdGhlIHN1aWNpZGUgcmF0ZXMgaW4gQWZyaWNhLgoKIyMjIyAzLjMuMy4zIEdlbmRlciBkaWZmZXJlbmNlcywgYnkgQ29udGluZW50CgpgYGB7cn0KZGF0YSAlPiUKICBncm91cF9ieShjb250aW5lbnQsIHNleCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCksIAogICAgICAgICAgICBzdWljaWRlcyA9IHN1bShhcy5udW1lcmljKHN1aWNpZGVzX25vKSksIAogICAgICAgICAgICBwb3B1bGF0aW9uID0gc3VtKGFzLm51bWVyaWMocG9wdWxhdGlvbikpLCAKICAgICAgICAgICAgc3VpY2lkZV9wZXJfMTAwayA9IChzdWljaWRlcyAvIHBvcHVsYXRpb24pICogMTAwMDAwLAogICAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb250aW5lbnQsIHkgPSBzdWljaWRlX3Blcl8xMDBrLCBmaWxsID0gc2V4KSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGdsb2JhbF9hdmVyYWdlLCBsaW5ldHlwZSA9IDIsIGNvbG9yID0gImdyZXkzNSIsIHNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJHZW5kZXIgRGlzcGFyaXR5LCBieSBDb250aW5lbnQiLAogICB4ID0gIkNvbnRpbmVudCIsIAogICB5ID0gIlN1aWNpZGVzIHBlciAxMDBrIiwgCiAgIGZpbGwgPSAiU2V4IikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKQmV0d2VlbiAxOTg1IGFuZCAyMDE1LCBFdXJvcGVhbiBtZW4gZmFjZWQgdGhlIGhpZ2hlc3QgcmlzayBvZiBzdWljaWRlLCB3aXRoIGEgcmF0ZSBvZiBhcHByb3hpbWF0ZWx5IDMwIHN1aWNpZGVzIHBlciAxMDAsMDAwIHBvcHVsYXRpb24gcGVyIHllYXIuIEluIGNvbXBhcmlzb24sIEFzaWEgaGFkIHRoZSBsb3dlc3Qgb3ZlcnJlcHJlc2VudGF0aW9uIG9mIG1hbGUgc3VpY2lkZSwgd2l0aCB0aGUgc3VpY2lkZSByYXRlIGZvciBtZW4gYmVpbmcgYXJvdW5kIDIuNSB0aW1lcyBoaWdoZXIgdGhhbiB0aGF0IGZvciB3b21lbi4gQ29udmVyc2VseSwgaW4gRXVyb3BlLCB0aGUgbWFsZSBzdWljaWRlIHJhdGUgd2FzIGFwcHJveGltYXRlbHkgMy45IHRpbWVzIGhpZ2hlciB0aGFuIHRoZSBmZW1hbGUgc3VpY2lkZSByYXRlLCBpbmRpY2F0aW5nIGEgZ3JlYXRlciBkaXNwYXJpdHkgYmV0d2VlbiBnZW5kZXJzIGluIHN1aWNpZGUgcmF0ZXMgY29tcGFyZWQgdG8gQXNpYS4KCiMjIyMgMy4zLjMuNCBHZW5kZXIgZGlmZmVyZW5jZXMsIGJ5IENvdW50cnkKCmBgYHtyfQojIE92ZXJhbGwgc3VpY2lkZSByYXRlIGJ5IGNvdW50cnkgYW5kIGNvbnRpbmVudApjb3VudHJ5X2xvbmcgPC0gZGF0YSAlPiUKICBncm91cF9ieShjb3VudHJ5LCBjb250aW5lbnQpICU+JQogIHN1bW1hcml6ZShzdWljaWRlX3Blcl8xMDBrID0gKHN1bShzdWljaWRlc19ubywgbmEucm0gPSBUUlVFKSAvIHN1bShwb3B1bGF0aW9uLCBuYS5ybSA9IFRSVUUpKSAqIDFlNSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgbXV0YXRlKHNleCA9ICJPVkVSQUxMIikKCiMgU3VpY2lkZSByYXRlIGJ5IGNvdW50cnksIGNvbnRpbmVudCwgYW5kIHNleApzZXhfY291bnRyeV9sb25nIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkoY291bnRyeSwgY29udGluZW50LCBzZXgpICU+JQogIHN1bW1hcml6ZShzdWljaWRlX3Blcl8xMDBrID0gKHN1bShzdWljaWRlc19ubywgbmEucm0gPSBUUlVFKSAvIHN1bShwb3B1bGF0aW9uLCBuYS5ybSA9IFRSVUUpKSAqIDFlNSwgLmdyb3VwcyA9ICJkcm9wIikKCiMgUGl2b3QgdGhlIGRhdGEgdG8gd2lkZSBmb3JtYXQgZm9yIHZpc3VhbGl6YXRpb24sIGFuZCBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBNYWxlIGFuZCBGZW1hbGUgc3VpY2lkZSByYXRlcwpzZXhfY291bnRyeV93aWRlIDwtIHNleF9jb3VudHJ5X2xvbmcgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNleCwgdmFsdWVzX2Zyb20gPSBzdWljaWRlX3Blcl8xMDBrKSAlPiUKICBhcnJhbmdlKE1hbGUgLSBGZW1hbGUpCgojIENvbnZlcnQgJ2NvdW50cnknIHRvIG9yZGVyZWQgZmFjdG9yIGJhc2VkIG9uIGRpZmZlcmVuY2UgaW4gc3VpY2lkZSByYXRlcyBiZXR3ZWVuIE1hbGUgYW5kIEZlbWFsZQpvcmRlcmVkX2NvdW50cmllcyA8LSBzZXhfY291bnRyeV93aWRlJGNvdW50cnkKc2V4X2NvdW50cnlfd2lkZSRjb3VudHJ5IDwtIGZhY3RvcihzZXhfY291bnRyeV93aWRlJGNvdW50cnksIG9yZGVyZWQgPSBUUlVFLCBsZXZlbHMgPSBvcmRlcmVkX2NvdW50cmllcykKc2V4X2NvdW50cnlfbG9uZyRjb3VudHJ5IDwtIGZhY3RvcihzZXhfY291bnRyeV9sb25nJGNvdW50cnksIG9yZGVyZWQgPSBUUlVFLCBsZXZlbHMgPSBvcmRlcmVkX2NvdW50cmllcykKCiMgVmlzdWFsaXphdGlvbgpnZ3Bsb3Qoc2V4X2NvdW50cnlfd2lkZSwgYWVzKHkgPSBjb3VudHJ5LCBjb2xvciA9IHNleCkpICsgCiAgZ2VvbV9kdW1iYmVsbChhZXMoeD1GZW1hbGUsIHhlbmQ9TWFsZSksIGNvbG9yID0gImdyZXkiLCBzaXplID0gMC41KSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHNleF9jb3VudHJ5X2xvbmcsIGFlcyh4ID0gc3VpY2lkZV9wZXJfMTAwayksIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBjb3VudHJ5X2xvbmcsIGFlcyh4ID0gc3VpY2lkZV9wZXJfMTAwayksIHNpemUgPSAwLjUpICsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZ2xvYmFsX2F2ZXJhZ2UsIGxpbmV0eXBlID0gMiwgY29sb3IgPSAiZ3JleTM1IiwgbGluZXdpZHRoID0gMC41KSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEpLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuMikpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCByb3VuZChtYXgoc2V4X2NvdW50cnlfd2lkZSRNYWxlLCBuYS5ybSA9IFRSVUUpICsgMTAsIC0xKSwgMTApKSArCiAgbGFicyh0aXRsZSA9ICJHZW5kZXIgRGlzcGFyaXR5LCBieSBDb250aW5lbnQgJiBDb3VudHJ5IiwgCiAgICAgICBzdWJ0aXRsZSA9ICJPcmRlcmVkIGJ5IGRpZmZlcmVuY2UgaW4gZGVhdGhzIHBlciAxMDBrLiIsIAogICAgICAgeCA9ICJTdWljaWRlcyBwZXIgMTAwayIsIAogICAgICAgeSA9ICJDb3VudHJ5IiwgCiAgICAgICBjb2xvciA9ICJTZXgiKQoKYGBgCgpgYGB7cn0KY291bnRyeV9nZW5kZXJfcHJvcCA8LSBzZXhfY291bnRyeV93aWRlICU+JQogIG11dGF0ZShNYWxlX1Byb3BvcnRpb24gPSBNYWxlIC8gKEZlbWFsZSArIE1hbGUpKSAlPiUKICBhcnJhbmdlKE1hbGVfUHJvcG9ydGlvbikKCnNleF9jb3VudHJ5X2xvbmckY291bnRyeSA8LSBmYWN0b3Ioc2V4X2NvdW50cnlfbG9uZyRjb3VudHJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjb3VudHJ5X2dlbmRlcl9wcm9wJGNvdW50cnkpCgpnZ3Bsb3Qoc2V4X2NvdW50cnlfbG9uZywgYWVzKHkgPSBzdWljaWRlX3Blcl8xMDBrLCB4ID0gY291bnRyeSwgZmlsbCA9IHNleCkpICsgCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAiUHJvcG9ydGlvbnMgb2Ygc3VpY2lkZXMgdGhhdCBhcmUgTWFsZSAmIEZlbWFsZSwgYnkgQ291bnRyeSIsIAogICAgICAgeCA9ICJDb3VudHJ5IiwgCiAgICAgICB5ID0gIlN1aWNpZGVzIHBlciAxMDBrIiwKICAgICAgIGZpbGwgPSAiU2V4IikgKyAKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lKAogIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjI1LCAiY20iKSwKICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPSA0KSkKYGBgClRoZSBvdmVyIHJlcHJlc2VudGF0aW9uIG9mIG1lbiBpbiBzdWljaWRlIGRlYXRocyBpcyBhIHdpZGVzcHJlYWQgcGhlbm9tZW5vbiBvYnNlcnZlZCBpbiB2YXJpb3VzIGNvdW50cmllcy4gQWx0aG91Z2ggd29tZW4gbWF5IGhhdmUgaGlnaGVyIHJhdGVzIG9mIGRlcHJlc3Npb24gYW5kIHN1aWNpZGFsIHRob3VnaHRzLCBpdCBpcyBtZW4gd2hvIGFyZSBtb3JlIGxpa2VseSB0byBkaWUgYnkgc3VpY2lkZS4gVGhpcyBwYXJhZG94aWNhbCBwYXR0ZXJuLCBrbm93biBhcyB0aGUgZ2VuZGVyIHBhcmFkb3ggaW4gc3VpY2lkYWwgYmVoYXZpb3IsIGhpZ2hsaWdodHMgdGhlIGNvbXBsZXggaW50ZXJwbGF5IG9mIGZhY3RvcnMgc3VjaCBhcyBzb2NpZXRhbCBleHBlY3RhdGlvbnMsIGhlbHAtc2Vla2luZyBiZWhhdmlvcnMsIGFuZCBjb3BpbmcgbWVjaGFuaXNtcyB0aGF0IGNvbnRyaWJ1dGUgdG8gdGhlIGdlbmRlciBkaXNwYXJpdHkgaW4gc3VpY2lkZSByYXRlcy4gSXQgdW5kZXJzY29yZXMgdGhlIG5lZWQgZm9yIGZ1cnRoZXIgcmVzZWFyY2ggYW5kIHRhcmdldGVkIGludGVydmVudGlvbnMgdG8gYWRkcmVzcyB0aGlzIGlzc3VlIGFuZCByZWR1Y2UgdGhlIGJ1cmRlbiBvZiBzdWljaWRlIGFtb25nIGJvdGggbWVuIGFuZCB3b21lbi4gW2xpbmtzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW5kZXJfZGlmZmVyZW5jZXNfaW5fc3VpY2lkZSkKCgojIDQuTW9kZWwKIyMgNC4xIG1vZGlmaWNhdGlvbiBmZXV0dXJlcyAKSW4gdGhpcyBjaGFwdGVyLCB3ZSBiZWdpbiB3aXRoIGEgY29tcHJlaGVuc2l2ZSBvdmVydmlldyBvZiBvdXIgdmFyaWFibGVzLCBpZGVudGlmeWluZyB0aGVpciBjaGFyYWN0ZXJpc3RpY3MgYW5kIHBvdGVudGlhbCBhcmVhcyBmb3IgcmVmaW5lbWVudC4gV2Ugc3Vic2VxdWVudGx5IG1ha2UgbmVjZXNzYXJ5IGFkanVzdG1lbnRzIHRvIGltcHJvdmUgdGhlaXIgc3VpdGFiaWxpdHkgZm9yIG91ciBhbmFseXNpcwoKYGBge3J9CmRhdGExIDwtZGF0YS5mcmFtZShkYXRhKQpgYGAKSW4gdGhlIGluaXRpYWwgcGhhc2Ugb2Ygb3VyIGFuYWx5c2lzLCB3ZSBmb2N1cyBvbiByZWZpbmluZyBvdXIgZGF0YXNldCBmb3IgbW9yZSBhY2N1cmF0ZSBhbmQgbWVhbmluZ2Z1bCByZXN1bHRzLiBTcGVjaWZpY2FsbHksIHdlIHJlbW92ZSBjZXJ0YWluIGNvbHVtbnMgdGhhdCBhcmUgbm90IGNvbnRyaWJ1dGluZyB0byBvdXIgdW5kZXJzdGFuZGluZyBvciBwcmVkaWN0aW9uIG9mIHRoZSBzdWljaWRlIHJhdGlvLgoKQXMgd2UndmUgZGlzY3Vzc2VkIGVhcmxpZXIsIHZhcmlhYmxlcyBzdWNoIGFzICdwb3B1bGF0aW9uJywgJ3N1aWNpZGVfbm8nLCBhbmQgdGhlaXIgcmVsYXRlZCB0cmFuc2Zvcm1hdGlvbnMgb3Igc2NhbGVkIHZlcnNpb25zIGluaGVyZW50bHkgaGF2ZSBhIHN0cm9uZyBhc3NvY2lhdGlvbiB3aXRoIG91ciB0YXJnZXQsIHRoZSAnc3VpY2lkZV9yYXRpbycuIFRoZSAnc3VpY2lkZV9yYXRpbycgaXMgYW4gZXN0aW1hdGUgb2YgdGhlIGxpa2VsaWhvb2Qgb2YgYW4gaW5kaXZpZHVhbCBjb21taXR0aW5nIHN1aWNpZGUgaW4gYSBzcGVjaWZpYyBkZW1vZ3JhcGhpYyBncm91cCBvciBjb3VudHJ5LgoKV2hpbGUgaXQgbWlnaHQgc2VlbSB0aGF0ICdwb3B1bGF0aW9uJyB3b3VsZCBiZSBhIGJlbmVmaWNpYWwgcHJlZGljdG9yIGZvciB0aGUgJ3N1aWNpZGVfcmF0aW8nLCBpbmNsdWRpbmcgaXQgbWF5IHNrZXcgb3VyIHJlc3VsdHMsIGxlYWRpbmcgdG8gYmlhc2VkIGVzdGltYXRpb25zLiBUaGlzIGlzIGJlY2F1c2UgaXQncyBub3QgdGhlIG1lcmUgc2l6ZSBvZiB0aGUgcG9wdWxhdGlvbiwgYnV0IHNwZWNpZmljIGNoYXJhY3RlcmlzdGljcyB3aXRoaW4gdGhhdCBwb3B1bGF0aW9uIHRoYXQgbGVhZCB0byBpbmNyZWFzZWQgc3VpY2lkZSByYXRpb3MuCgpUbyBuYXZpZ2F0ZSB0aGlzIGNoYWxsZW5nZSwgd2UgaW5jb3Jwb3JhdGUgZGlmZmVyZW50IGZlYXR1cmVzIHRvIHRyYW5zZm9ybSBhbmQgY29udmV5IHRoZSBjcnVjaWFsIGluZm9ybWF0aW9uIGNvbnRhaW5lZCBpbiB0aGVzZSB2YXJpYWJsZXMsIHdpdGhvdXQgZGlyZWN0bHkgdXNpbmcgdGhlbS4gVGhpcyB3YXksIHdlIGFpbSB0byBjcmVhdGUgYSBtb2RlbCB0aGF0IGNhcHR1cmVzIHRoZSBudWFuY2VzIGFuZCBjb21wbGV4aXR5IG9mIHRoZSBmYWN0b3JzIGNvbnRyaWJ1dGluZyB0byB0aGUgc3VpY2lkZSByYXRpby4KYGBge3J9CnJlbW92ZV92YXIgPSBjKCJzcXJ0X3N1aWNpZGVfbm8iLCJwb3B1bGF0aW9uIiwibG9nX3BvcHVsYXRpb24iLCJuZXdfc3VpY2lkZXNfbm8iLCJuZXdfc3VpY2lkZV9yYXRpbyIsInNjYWxlZF9wb3B1bGF0aW9uIiwic2NhbGVkX2xvZ19wb3B1bGF0aW9uIiwic2NhbGVkX0dEUF9mb3JfeWVhciIsInNjYWxlZF9sb2dfR0RQX3llYXIiLCJsb2dfc3VpY2lkZV9ubyIsInNjYWxlZF9sb2dfR0RQX2NhcGl0YSIsInNxcnRfcG9wdWxhdGlvbiIsInNxcnRfc3VpY2lkZV9ubyIsInN1aWNpZGVzX25vIiApCmRhdGExIDwtIGRwbHlyOjpzZWxlY3QoZGF0YTEsIC1kcGx5cjo6b25lX29mKHJlbW92ZV92YXIpKQoKYGBgCkluIHRoZSBzZWN0aW9uIGRlZGljYXRlZCB0byBvdXRsaWVyIGFuYWx5c2lzLCB3ZSBvYnNlcnZlZCB0aGF0IHRoZSBsb2ctdHJhbnNmb3JtZWQgdmVyc2lvbiBvZiAnc3VpY2lkZV9yYXRpbycgaXMgc2lnbmlmaWNhbnRseSBtb3JlIHJlc2lsaWVudCB0byBvdXRsaWVycyB0aGFuIHRoZSBvcmlnaW5hbCAnc3VpY2lkZV9yYXRpbycgdmFyaWFibGUuIFRoaXMgZGlzY292ZXJ5IG1ha2VzICdsb2coc3VpY2lkZV9yYXRpbyknIGEgcHJlZmVycmVkIGNhbmRpZGF0ZSBmb3Igb3VyIHRhcmdldCB2YXJpYWJsZSwgZXNwZWNpYWxseSBnaXZlbiBpdHMgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aGljaCBpcyBhIGRlc2lyYWJsZSBwcm9wZXJ0eSBmb3IgbWFueSBzdGF0aXN0aWNhbCBtb2RlbHMuCgpUbyBwcmVwYXJlIG91ciBkYXRhc2V0IGZvciBmdXJ0aGVyIG1vZGVsaW5nLCB3ZSBzdGFuZGFyZGl6ZSBvdXIgcHJlZGljdG9ycyBieSByZXNjYWxpbmcgdGhlbSB0byBoYXZlIGEgbWVhbiBvZiB6ZXJvIGFuZCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBvbmUuRmluYWxseSwgdG8gbWFpbnRhaW4gYSB0aWR5IGRhdGFzZXQsIHdlIGRyb3AgdGhlIG9yaWdpbmFsIHVudHJhbnNmb3JtZWQgYW5kIHVuc2NhbGVkIGNvbHVtbnMuIFRoaXMgbGVhdmVzIHVzIHdpdGggYSBjbGVhbiwgc3RhbmRhcmRpemVkIGRhdGFzZXQgdGhhdCBpcyByZWFkeSBmb3IgdGhlIG5leHQgc3RhZ2VzIG9mIG91ciBhbmFseXNpcyBhbmQgbW9kZWxpbmcgcHJvY2Vzcy4KYGBge3J9CnNjYWxlX2NvbHVtbnMgPC0gZnVuY3Rpb24oZGF0YSwgY29sdW1uc190b19zY2FsZSkgewogICMgTG9vcCBvdmVyIHRoZSBjb2x1bW5zCiAgZm9yIChjb2wgaW4gY29sdW1uc190b19zY2FsZSkgewogICAgIyBDaGVjayBpZiB0aGUgY29sdW1uIGV4aXN0cyBhbmQgaXMgbm90IGFsbCBOQQogICAgaWYgKCFjb2wgJWluJSBuYW1lcyhkYXRhKSB8fCBhbGwoaXMubmEoZGF0YVtbY29sXV0pKSkgewogICAgICBtZXNzYWdlKHBhc3RlKCJDb2x1bW4iLCBjb2wsICJkb2VzIG5vdCBleGlzdCBvciBpcyBhbGwgTkEuIFNraXBwaW5nLi4uIikpCiAgICAgIG5leHQKICAgIH0KICAgICMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBuYW1lCiAgICBuZXdfY29sX25hbWUgPC0gcGFzdGUwKCJzY2FsZWRfIiwgY29sKQogICAgCiAgICAjIFNjYWxlIHRoZSBjb2x1bW4KICAgIGRhdGFbW25ld19jb2xfbmFtZV1dIDwtIHNjYWxlKGRhdGFbW2NvbF1dKQogIH0KICAKICAjIERyb3AgdGhlIG9yaWdpbmFsIGNvbHVtbnMKICBkYXRhIDwtIGRhdGFbLCAhKG5hbWVzKGRhdGEpICVpbiUgY29sdW1uc190b19zY2FsZSldCiAgCiAgcmV0dXJuKGRhdGEpCn0KCmBgYAoKYGBge3J9CnNjYWxlZF92YXIgPSBjKCJHRFBfZm9yX3llYXIiLCJHRFBfcGVyX2NhcGl0YSIsImxpZmVfZXhwIiwiYXZnX3RlbXAiLCJtaW5fdGVtcCIsIm1heF90ZW1wIiwibG9nX0dEUF95ZWFyIiwibG9nX0dEUF9jYXBpdGEiLCJzcXJ0X0dEUF95ZWFyIiwic3FydF9HRFBfY2FwaXRhIiwibG9nX3N1aWNpZGVfcmF0aW8iLCJzdWljaWRlX3JhdGlvIiwic3FydF9zdWljaWRlX3JhdGlvIikKZGF0YTIgPC0gc2NhbGVfY29sdW1ucyhkYXRhMSxzY2FsZWRfdmFyKQoKYGBgCiMjIDQuMiBNdWx0aWNvbGxpbmVhcml0eSAKIyMjIDQuMi4xIENvbnRpbnVvdXMgVmFyaWFibGUgCkluIHRoaXMgc2VjdGlvbiwgd2UgZW1wbG95IHRlY2huaXF1ZXMgc3VjaCBhcyBoZWF0bWFwcyBhbmQgVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvciAoVklGKSB0byBpbnZlc3RpZ2F0ZSBwb3RlbnRpYWwgY29sbGluZWFyaXR5IGFtb25nIG91ciB2YXJpYWJsZXMuCgpBIGhlYXRtYXAgaXMgYSB2YWx1YWJsZSB2aXN1YWxpemF0aW9uIHRvb2wgdGhhdCBpbGx1c3RyYXRlcyB0aGUgY29ycmVsYXRpb24gbWF0cml4IHRocm91Z2ggYSBncmFkaWVudCBjb2xvciBzY2hlbWUuIEJ5IHZpc3VhbGx5IHJlcHJlc2VudGluZyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzLCBhIGhlYXRtYXAgY2FuIHJldmVhbCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyBhbW9uZyB2YXJpYWJsZXMsIGhpZ2hsaWdodGluZyBhbnkgcG90ZW50aWFsIG11bHRpY29sbGluZWFyaXR5IGlzc3Vlcy4KCk9uIHRoZSBvdGhlciBoYW5kLCBWSUYgaXMgYSBudW1lcmljYWwgbWVhc3VyZSB0aGF0IHF1YW50aWZpZXMgdGhlIHNldmVyaXR5IG9mIG11bHRpY29sbGluZWFyaXR5IGluIGEgcmVncmVzc2lvbiBhbmFseXNpcy4gSXQgZ2F1Z2VzIHRoZSBhbW91bnQgb2YgbXVsdGljb2xsaW5lYXJpdHkgYnkgZXhhbWluaW5nIGhvdyBtdWNoIHRoZSB2YXJpYW5jZSBvZiB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGlzIGluY3JlYXNlZCBkdWUgdG8gbXVsdGljb2xsaW5lYXJpdHkuIEEgaGlnaCBWSUYgc3VnZ2VzdHMgYSBoaWdoIGRlZ3JlZSBvZiBjb2xsaW5lYXJpdHkgd2l0aCBvdGhlciB2YXJpYWJsZXMsIHdhcnJhbnRpbmcgYXR0ZW50aW9uLgoKVGhlc2UgdGVjaG5pcXVlcyBjb2xsZWN0aXZlbHkgZ2l2ZSB1cyBhIGhvbGlzdGljIHZpZXcgb2YgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBhbW9uZyBvdXIgdmFyaWFibGVzLCBhaWRpbmcgaW4gZmVhdHVyZSBzZWxlY3Rpb24gYW5kIG1vZGVsIHBlcmZvcm1hbmNlIGltcHJvdmVtZW50LgoKYGBge3J9CiMgd2UgY3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB3aGljaCBvbmx5IGluY2x1ZGVzIG51bWVyaWMgY29sdW1ucyB1c2luZyBzYXBwbHkKbnVtZXJpY19kYXRhIDwtIGRhdGEyW3NhcHBseShkYXRhMiwgaXMubnVtZXJpYyldCgoKd2lkdGggPC0gMjUKaGVpZ2h0IDwtIDI1Cm9wdGlvbnMocmVwci5wbG90LndpZHRoID0gd2lkdGgsIHJlcHIucGxvdC5oZWlnaHQgPSBoZWlnaHQpCgoKY29ycl9tYXRyaXggPC0gY29yKG51bWVyaWNfZGF0YSkKCiMgUm91bmQgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCB0byAzIGRlY2ltYWwgcGxhY2VzCnJvdW5kZWRfY29yciA8LSByb3VuZChjb3JyX21hdHJpeCwgMykKCiMgQ3JlYXRlIHRoZSBjb3JyZWxhdGlvbiBwbG90CmdnY29ycnBsb3Qocm91bmRlZF9jb3JyLCAKICAgICAgICAgICBsYWIgPSBUUlVFLCAKICAgICAgICAgICBsYWJfc2l6ZSA9IDEuNSwgCiAgICAgICAgICAgbWV0aG9kID0gImNpcmNsZSIsIAogICAgICAgICAgIHBjaCA9IDEsIAogICAgICAgICAgIGNvbG9ycyA9IGMoInJlZCIsICIjZWJlYmViIiwgIiMxMzUyN2EiKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpCgpgYGAKdGhlcmUgaXMgc3Ryb25nIGNvcnJpbGF0aW9uIGJldHdlZW4gc29tZSBvZiB0aGUgdmFyaWFibGVzCgojIyMjIyBWSUYKCmBgYHtyfQpyZW1vdmVfdmFyIDwtIGMoInNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8iLCJzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8iKQpkYXRhX3NpbXBsZSA8LWRwbHlyOjpzZWxlY3QobnVtZXJpY19kYXRhLCAtZHBseXI6Om9uZV9vZihyZW1vdmVfdmFyKSkKcmVtb3ZlX3ZhciA8LSBjKCJzY2FsZWRfc3FydF9zdWljaWRlX3JhdGlvIiwic2NhbGVkX3N1aWNpZGVfcmF0aW8iKQpkYXRhX2xvZyA8LWRwbHlyOjpzZWxlY3QobnVtZXJpY19kYXRhLCAtZHBseXI6Om9uZV9vZihyZW1vdmVfdmFyKSkKcmVtb3ZlX3ZhciA8LSBjKCJzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8iLCJzY2FsZWRfc3VpY2lkZV9yYXRpbyIpCmRhdGFfc3FydCA8LWRwbHlyOjpzZWxlY3QobnVtZXJpY19kYXRhLCAtZHBseXI6Om9uZV9vZihyZW1vdmVfdmFyKSkKCmBgYAphbmQgZm9yIGVhY2ggdGFyZ2V0IHdlIG1ha2UgYSB2aWYgZ3JhcGgKYGBge3J9Cm1vZC5saW5lYXIgPC0gbG0oc2NhbGVkX3N1aWNpZGVfcmF0aW9+IC4sIGRhdGEgPSBkYXRhX3NpbXBsZSkKdmlmcyA8LSBkYXRhLmZyYW1lKHZpZihtb2QubGluZWFyKSkKCmdncGxvdCh2aWZzLCBhZXMoeT12aWYubW9kLmxpbmVhci4sIHg9cm93Lm5hbWVzKHZpZnMpKSkgKyAKICAgIGdlb21fYmFyKGFlcyhmaWxsPXZpZi5tb2QubGluZWFyLj41KSxzdGF0PSJpZGVudGl0eSIpKwogICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gInNxcnQiLCAgYnJlYWtzID0gYyg1LCAxMCwgNTAsIDEwMCkpKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNSwgY29sb3VyID0gInJlZCIpICsgCiAgICBnZ3RpdGxlKCJWSUYgcGVyIGZlYXR1cmUgZm9yIHN1aWNpZGVfcmF0aW8gYXMgdGFyZ2V0IikgKwogICAgeGxhYigiRmVhdHVycyIpICsgeWxhYigiVklGIikgKwogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTIwLCBoanVzdD0xKSkrCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmRZbEJ1IikKYGBgCkFzIG9ic2VydmFibGUgZnJvbSBvdXIgYW5hbHlzaXMsIG51bWVyb3VzIHZhcmlhYmxlcyBleGhpYml0IGhpZ2ggVklGLCBhIHNpZ24gb2YgbXVsdGljb2xsaW5lYXJpdHkuIFRoaXMgaXNuJ3Qgc3VycHJpc2luZyBnaXZlbiB0aGF0IG1hbnkgdmFyaWFibGVzIHdlcmUgZGVyaXZlZCBmcm9tIG9uZSBhbm90aGVyIHRocm91Z2ggdHJhbnNmb3JtYXRpb25zLiBUbyBoYW5kbGUgdGhpcywgd2UgbmVlZCB0byBlbXBsb3kgYSBzdHJhdGVneSBvZiB2YXJpYWJsZSBjbHVzdGVyaW5nLiBFYWNoIGNsdXN0ZXIgd291bGQgY29udGFpbiB2YXJpYWJsZXMgdGhhdCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBvbmUgYW5vdGhlciwgaW5kaWNhdGluZyBwb3RlbnRpYWwgbXVsdGljb2xsaW5lYXJpdHkuCgpGcm9tIGVhY2ggY2x1c3Rlciwgd2Ugd291bGQgdGhlbiBzZWxlY3QgdGhlIHZhcmlhYmxlIHRoYXQgc3RyaWtlcyB0aGUgYmVzdCBiYWxhbmNlIGJldHdlZW4gaGF2aW5nIHRoZSBoaWdoZXN0IGNvcnJlbGF0aW9uIHdpdGggb3VyIHRhcmdldCBhbmQgdGhlIGxlYXN0IHN1c2NlcHRpYmlsaXR5IHRvIG91dGxpZXJzLiBUaGlzIGFwcHJvYWNoIGVuYWJsZXMgdXMgdG8gbWFpbnRhaW4gZXNzZW50aWFsIGluZm9ybWF0aW9uIHdoaWxlIG1pdGlnYXRpbmcgdGhlIG5lZ2F0aXZlIGVmZmVjdHMgb2YgbXVsdGljb2xsaW5lYXJpdHksIHRoZXJlYnkgZW5oYW5jaW5nIG91ciBtb2RlbCdzIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuCmBgYHtyfQpnZHBfdmFyIDwtYygic2NhbGVkX0dEUF9mb3JfeWVhciIsInNjYWxlZF9HRFBfcGVyX2NhcGl0YSIsInNjYWxlZF9sb2dfR0RQX3llYXIiLCAic2NhbGVkX2xvZ19HRFBfY2FwaXRhIiwic2NhbGVkX3NxcnRfR0RQX3llYXIiLCJzY2FsZWRfc3FydF9HRFBfY2FwaXRhIikKdGVtcF92YXI8LWMoInNjYWxlZF9taW5fdGVtcCIsInNjYWxlZF9hdmdfdGVtcCIsInNjYWxlZF9tYXhfdGVtcCIpCmBgYApmb3Igc3VpY2lkZV9yYXRpbyBzY2FsZWRfbWluX3RlbXAgYW5kIHNjYWxlZF9HUERfcGVyX3llYXIKbGV0cyBkbyB0aGlzIHRlc3QgYWdhaW4gCmBgYHtyfQptb2QubGluZWFyIDwtIGxtKHNjYWxlZF9zdWljaWRlX3JhdGlvfiAuLCBkYXRhID0gc3Vic2V0KChkYXRhX3NpbXBsZSksc2VsZWN0ID0gYyh5ZWFyLHNjYWxlZF9saWZlX2V4cCxzY2FsZWRfbWluX3RlbXAsc2NhbGVkX0dEUF9mb3JfeWVhcixzY2FsZWRfc3VpY2lkZV9yYXRpbykpKQp2aWZzIDwtIGRhdGEuZnJhbWUodmlmKG1vZC5saW5lYXIpKQpnZ3Bsb3QodmlmcywgYWVzKHk9dmlmLm1vZC5saW5lYXIuLCB4PXJvdy5uYW1lcyh2aWZzKSkpICsgCiAgICBnZW9tX2JhcihhZXMoZmlsbD12aWYubW9kLmxpbmVhci4+NSksc3RhdD0iaWRlbnRpdHkiKSsKICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJzcXJ0IiwgIGJyZWFrcyA9IGMoNSwgMTAsIDUwLCAxMDApKSsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUsIGNvbG91ciA9ICJyZWQiKSArIAogICAgZ2d0aXRsZSgiVklGIHBlciBmZWF0dXJlIGZvciBzdWljaWRlX3JhdGlvIGFzIHRhcmdldCIpICsKICAgIHhsYWIoIkZlYXR1cnMiKSArIHlsYWIoIlZJRiIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0yMCwgaGp1c3Q9MSkpKwogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSsKICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlJkWWxCdSIpCmBgYAp3ZSBkbyBzYW1lIGZvciBsb2dfc3VpY2lkZV9yYXRpbyBhbmQgc3FydF9zdWljaWRlX3JhdGlvIApmb3IgbG9nX3N1aWNpZGVfcmF0aW8gc2NhbGVkX2F2Z190ZW1wIGFuZCBzY2FsZWRfbG9nX0dEcF9jYXBpdGEKc3VpY2lkZV9yYXRpbyBzY2FsZWRfbWluX3RlbXAgYW5kIHNjYWxlZF9HUERfcGVyX3llYXIKbGV0cyBkbyB0aGlzIHRlc3QgYWdhaW4gCmBgYHtyfQptb2QubGluZWFyIDwtIGxtKHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpb34gLiwgZGF0YSA9IHN1YnNldCgoZGF0YV9sb2cpLHNlbGVjdCA9IGMoeWVhcixzY2FsZWRfbGlmZV9leHAsc2NhbGVkX2F2Z190ZW1wLHNjYWxlZF9sb2dfR0RQX2NhcGl0YSxzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8pKSkKdmlmcyA8LSBkYXRhLmZyYW1lKHZpZihtb2QubGluZWFyKSkKCmdncGxvdCh2aWZzLCBhZXMoeT12aWYubW9kLmxpbmVhci4sIHg9cm93Lm5hbWVzKHZpZnMpKSkgKyAKICAgIGdlb21fYmFyKGFlcyhmaWxsPXZpZi5tb2QubGluZWFyLj41KSxzdGF0PSJpZGVudGl0eSIpKwogICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gInNxcnQiLCAgYnJlYWtzID0gYyg1LCAxMCwgNTAsIDEwMCkpKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNSwgY29sb3VyID0gInJlZCIpICsgCiAgICBnZ3RpdGxlKCJWSUYgcGVyIGZlYXR1cmUgZm9yIHN1aWNpZGVfcmF0aW8gYXMgdGFyZ2V0IikgKwogICAgeGxhYigiRmVhdHVycyIpICsgeWxhYigiVklGIikgKwogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTIwLCBoanVzdD0xKSkrCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmRZbEJ1IikKYGBgCgpzdWljaWRlX3JhdGlvIHNjYWxlZF9taW5fdGVtcCBhbmQgc2NhbGVkX0dQRF9wZXJfeWVhcgpmb3IgbG9nX3N1aWNpZGVfcmF0aW8gc2NhbGVkX2F2Z190ZW1wIGFuZCBzY2FsZWRfbG9nX0dEcF9jYXBpdGEKZm9yIHNxcnRfc3VpY2lkZV9yYXRpbyBzY2FsZWRfYXZnX3RlbXAgYW5kIHNjYWxlZF9sb2dfR0RwX2NhcGl0YQpsZXRzIGRvIHRoaXMgdGVzdCBhZ2FpbiAKYGBge3J9Cm1vZC5saW5lYXIgPC0gbG0oc2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpb34gLiwgZGF0YSA9IHN1YnNldCgoZGF0YV9zcXJ0KSxzZWxlY3QgPSBjKHllYXIsc2NhbGVkX2xpZmVfZXhwLHNjYWxlZF9sb2dfR0RQX3llYXIsc2NhbGVkX2F2Z190ZW1wLHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8pKSkKdmlmcyA8LSBkYXRhLmZyYW1lKHZpZihtb2QubGluZWFyKSkKCmdncGxvdCh2aWZzLCBhZXMoeT12aWYubW9kLmxpbmVhci4sIHg9cm93Lm5hbWVzKHZpZnMpKSkgKyAKICAgIGdlb21fYmFyKGFlcyhmaWxsPXZpZi5tb2QubGluZWFyLj41KSxzdGF0PSJpZGVudGl0eSIpKwogICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gInNxcnQiLCAgYnJlYWtzID0gYyg1LCAxMCwgNTAsIDEwMCkpKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNSwgY29sb3VyID0gInJlZCIpICsgCiAgICBnZ3RpdGxlKCJWSUYgcGVyIGZlYXR1cmUgZm9yIHN1aWNpZGVfcmF0aW8gYXMgdGFyZ2V0IikgKwogICAgeGxhYigiRmVhdHVycyIpICsgeWxhYigiVklGIikgKwogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTIwLCBoanVzdD0xKSkrCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmRZbEJ1IikKYGBgCgphcyB3ZSBjYW4gc2VlIGFsbCBvZiB0aGVtIGhhdmUgdmFsdWUgbGVzcyB0aGFuIDUgYW5kIHdlIGNhbiBzYXkgdGhhdCB0aGVyZSBpcyBubyBjb2xpbmlhcml0eSBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlLgojIyMgNC4yLjIgQ2F0ZWdvcmljYWwgdmFyaWFibGUgClRoZSBjb25jZXB0IG9mIG11bHRpY29sbGluZWFyaXR5IGlzIGEgYml0IGxlc3Mgc3RyYWlnaHRmb3J3YXJkIHdoZW4gYXBwbGllZCB0byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIHBhcnRpY3VsYXJseSBiZWNhdXNlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBjYW4gdGFrZSBvbiBsaW1pdGVkLCBhbmQgdXN1YWxseSBmZXcsIGRpc3RpbmN0IHZhbHVlcy4KCkhvd2V2ZXIsIG11bHRpY29sbGluZWFyaXR5IGNhbiBzdGlsbCBvY2N1ciB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gRm9yIGV4YW1wbGUsIHN1cHBvc2UgeW91IGhhdmUgYSBkYXRhc2V0IG9mIGNhcnMsIGFuZCB5b3UgaGF2ZSB0d28gdmFyaWFibGVzOiAiQnJhbmQiIGFuZCAiQ291bnRyeSIuIElmIGV2ZXJ5ICJCcmFuZCIgdW5pcXVlbHkgbWFwcyB0byBhICJDb3VudHJ5IiAoZS5nLiwgaWYgJ1RveW90YScgaXMgYWx3YXlzICdKYXBhbicsICdGb3JkJyBpcyBhbHdheXMgJ1VTQScsIGV0Yy4pLCB0aGVuIHRoZXNlIHR3byB2YXJpYWJsZXMgYXJlIHBlcmZlY3RseSBtdWx0aWNvbGxpbmVhci4Kd2UgY2FuIHVzZSBjaGkgc3F1cmUgYnV0IGNoaXNxIGlzIHZlcnkgc2Vuc2V0aXZlIHRvIHVuYmFsYW5jZWQgdmFyaWFibGUuCndlIHdpbGwgdWVzIENyYW3DqXIncyBWIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIApDcmFtw6lyJ3MgViBpcyBhIHN0YXRpc3RpY2FsIG1lYXN1cmUgdGhhdCBhc3Nlc3NlcyB0aGUgc3RyZW5ndGggb2YgYXNzb2NpYXRpb24gYmV0d2VlbiB0d28gbm9taW5hbCB2YXJpYWJsZXMuIEl0IGlzIGJhc2VkIG9uIFBlYXJzb24ncyBjaGktc3F1YXJlZCBzdGF0aXN0aWMgYW5kIHdhcyBwdWJsaXNoZWQgYnkgSGFyYWxkIENyYW3DqXIgaW4gMTk0Ni4KICAKQ3JhbcOpcidzIFYgcmFuZ2VzIGZyb20gMCAoaW5kaWNhdGluZyBubyBhc3NvY2lhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMpIHRvIDEgKGluZGljYXRpbmcgYSBwZXJmZWN0IGFzc29jaWF0aW9uKS4gSXQgY291bGQgYmUgc2VlbiBhcyBhbiBleHRlbnNpb24gb2YgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IHRvIG5vbWluYWwgZGF0YS4KCkNyYW3DqXIncyBWIGlzIHN5bW1ldHJpY2FsIOKAlCBpdCBkb2VzIG5vdCBtYXR0ZXIgd2hpY2ggdmFyaWFibGUgd2UgY29uc2lkZXIgYXMgaW5kZXBlbmRlbnQgb3IgZGVwZW5kZW50LiBUaGUgZm9ybXVsYSBmb3IgQ3JhbcOpcidzIFYgaXM6CgpWID0gc3FydCgoWF4yL24pIC8gKG1pbihrLTEsIHItMSkpKQoKd2hlcmU6CgpYXjIgaXMgdGhlIGNoaS1zcXVhcmVkIHN0YXRpc3RpYywKbiBpcyB0aGUgdG90YWwgc2FtcGxlIHNpemUsCmsgaXMgdGhlIG51bWJlciBvZiBjb2x1bW5zLApyIGlzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgY29udGluZ2VuY3kgdGFibGUuCkp1c3QgbGlrZSB3aXRoIGNvcnJlbGF0aW9uLCBhIHZhbHVlIGNsb3NlIHRvIDAgaW5kaWNhdGVzIGxpdHRsZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMsIGFuZCBhIHZhbHVlIGNsb3NlIHRvIDEgaW5kaWNhdGVzIGEgc3Ryb25nIGFzc29jaWF0aW9uLiBIb3dldmVyLCB1bmxpa2UgY29ycmVsYXRpb24sIENyYW3DqXIncyBWIGNhbiBvbmx5IHJlYWNoIDEgaW4gdGhlIGNhc2Ugb2YgY29tcGxldGUgYXNzb2NpYXRpb24gKGFsbCBjZWxscyBvdGhlciB0aGFuIHRoZSBkaWFnb25hbCBhcmUgMCksIG9yIHdoZW4gdGhlIG51bWJlciBvZiByb3dzIGVxdWFscyB0aGUgbnVtYmVyIG9mIGNvbHVtbnMuCmZpcnN0IHNlcGVyYXRlIGNhdGVnb3JpY2FsIHZhcgpgYGB7cn0KZmFjdG9yX3ZhcnMgPC0gc2FwcGx5KGRhdGEsIGlzLmZhY3RvcikKCmZhY3Rvcl92YXJzX25hbWVzIDwtIG5hbWVzKGRhdGEpW2ZhY3Rvcl92YXJzXQpgYGAKYGBge3J9CmZhY3Rvcl92YXJzX25hbWVzCmBgYAp0aGVuIHdlIGFwcGx5IENyYW3DqXIncyBWIGZvciBlYWNoIHBhaXIgb2YgdGhpcyB2YXJpYWJsZS4gCgpgYGB7cn0KIyBSZXRyaWV2ZSBhbGwgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIG5hbWVzCmZhY3Rvcl92YXJzX25hbWVzIDwtIG5hbWVzKGRhdGFbc2FwcGx5KGRhdGEsIGlzLmZhY3RvcildKQoKIyBJbml0aWFsaXplIGEgZGF0YSBmcmFtZSB0byBob2xkIHRoZSBDcmFtZXIncyBWIHZhbHVlcwpWX2RmIDwtIGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBsZW5ndGgoZmFjdG9yX3ZhcnNfbmFtZXMpLCBuY29sID0gbGVuZ3RoKGZhY3Rvcl92YXJzX25hbWVzKSkpCm5hbWVzKFZfZGYpIDwtIGZhY3Rvcl92YXJzX25hbWVzCnJvd25hbWVzKFZfZGYpIDwtIGZhY3Rvcl92YXJzX25hbWVzCgojIExvb3Agb3ZlciBlYWNoIHBhaXIgb2YgdmFyaWFibGVzCmZvcihpIGluIDE6bGVuZ3RoKGZhY3Rvcl92YXJzX25hbWVzKSl7CiAgZm9yKGogaW4gMTpsZW5ndGgoZmFjdG9yX3ZhcnNfbmFtZXMpKXsKICAgIGlmKGkgIT0gail7CiAgICAgIAogICAgICAjIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCiAgICAgIHRhYiA8LSB0YWJsZShkYXRhW1tmYWN0b3JfdmFyc19uYW1lc1tpXV1dLCBkYXRhW1tmYWN0b3JfdmFyc19uYW1lc1tqXV1dKQogICAgICAKICAgICAgIyBQZXJmb3JtIENoaS1zcXVhcmUgdGVzdAogICAgICBjaGlfc3EgPC0gY2hpc3EudGVzdCh0YWIpCiAgICAgIAogICAgICAjIENhbGN1bGF0ZSBDcmFtZXIncyBWCiAgICAgIG4gPC0gc3VtKHRhYikgIyB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCiAgICAgIGsgPC0gbWluKGRpbSh0YWIpKSAjIG51bWJlciBvZiByb3dzIG9yIGNvbHVtbnMgKHdoaWNoZXZlciBpcyBzbWFsbGVyKQogICAgICBWIDwtIHNxcnQoY2hpX3NxJHN0YXRpc3RpYyAvIChuICogKGsgLSAxKSkpCiAgICAgIAogICAgICBWX2RmW2ksal0gPC0gVgogICAgICAKICAgICAgY2F0KCJDcmFtZXIncyBWIGZvciIsIGZhY3Rvcl92YXJzX25hbWVzW2ldLCAiYW5kIiwgZmFjdG9yX3ZhcnNfbmFtZXNbal0sICI6IiwgViwgIlxuIikKICAgICAgCiAgICB9IGVsc2UgewogICAgICBWX2RmW2ksal0gPC0gTkEKICAgIH0KICB9Cn0KCiMgUmVwbGFjZSBOQSB2YWx1ZXMgd2l0aCAwClZfZGZbaXMubmEoVl9kZildIDwtIDAKCgpwcmludChWX2RmKQoKYGBgCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoInBoZWF0bWFwIikKCmBgYApgYGB7cn0KbGlicmFyeShwaGVhdG1hcCkKCiMgTWFrZSB0aGUgaGVhdG1hcApwaGVhdG1hcChWX2RmLCBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygibmF2eSIsICJ3aGl0ZSIsICJmaXJlYnJpY2szIikpKDI1KSkKYGBgCkFzIG9ic2VydmVkLCB0aGUgJ2NvdW50cnknIHZhcmlhYmxlIGRlbW9uc3RyYXRlcyBzaWduaWZpY2FudCBhc3NvY2lhdGlvbnMgd2l0aCBudW1lcm91cyB2YXJpYWJsZXMuIFRoaXMgaXMgZXhwZWN0ZWQgZ2l2ZW4gdGhhdCB0aGVzZSB2YXJpYWJsZXMgd2VyZSBjcmVhdGVkIHZpYSBhICdncm91cF9ieScgb3BlcmF0aW9uIG9uICdjb3VudHJ5Jy4KCkhvd2V2ZXIsIHRoZSBjcnVjaWFsIG9ic2VydmF0aW9uIGlzIHRoZSBzdWJzdGFudGlhbCBhc3NvY2lhdGlvbiBhbW9uZyAnYXZnX3RlbXBfYmluZV9qZW5rcycsICdtaW5fdGVtcF9iaW5lX2plbmtzJywgYW5kICdtYXhfdGVtcF9iaW5lX2plbmtzJy4gRm9yIG1vZGVsIGVmZmljaWVuY3ksIHdlIHNob3VsZCBzZWxlY3Qgb25lIGZyb20gdGhpcyBzZXQuCgpUbyBndWlkZSB0aGlzIHNlbGVjdGlvbiwgd2UgcmFuIHNldmVyYWwgbGluZWFyIG1vZGVscyB0byBldmFsdWF0ZSBjb21wYXRpYmlsaXR5IGJldHdlZW4gdGhlc2UgdGVtcGVyYXR1cmUgdmFyaWFibGVzIGFuZCBvdXIgcG90ZW50aWFsIHRhcmdldHMgKCdzdWljaWRlX3JhdGlvJywgJ2xvZ19zdWljaWRlX3JhdGlvJywgYW5kICdzcXJ0X3N1aWNpZGVfcmF0aW8nKS4KCldlIGNvbXBpbGVkIGEgZGF0YWZyYW1lIGZlYXR1cmluZyBvdXIgdGhyZWUgdGFyZ2V0cyBhbmQgdGhlIHRocmVlIHRlbXBlcmF0dXJlIHZhcmlhYmxlcy4gVGhlIGRhdGFmcmFtZSBlbnRyaWVzIHJlcHJlc2VudCB0aGUgYWRqdXN0ZWQgUi1zcXVhcmVkIHZhbHVlcyBmb3IgZWFjaCBjb3JyZXNwb25kaW5nIHBhaXIsIHByb3ZpZGluZyBhIGJhc2lzIGZvciBvcHRpbWFsIGZlYXR1cmUgc2VsZWN0aW9uLgpgYGB7cn0KdGFyZ2V0cyA8LSBjKCJzY2FsZWRfc3VpY2lkZV9yYXRpbyIsICJzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8iLCAic2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpbyIpCnZhcmlhYmxlcyA8LSBjKCJhdmdfdGVtcF9iaW5lX2plbmtzIiwibWluX3RlbXBfYmluZV9qZW5rcyIsICJtYXhfdGVtcF9iaW5lX2plbmtzIgopCgphZGp1c3RlZF9yMiA8LSBtYXRyaXgobnJvdyA9IGxlbmd0aCh0YXJnZXRzKSwgbmNvbCA9IGxlbmd0aCh2YXJpYWJsZXMpKQpyb3duYW1lcyhhZGp1c3RlZF9yMikgPC0gdGFyZ2V0cwpjb2xuYW1lcyhhZGp1c3RlZF9yMikgPC0gdmFyaWFibGVzCgojIGxvb3Agb3ZlciBlYWNoIHRhcmdldCBhbmQgdmFyaWFibGUKZm9yICh0YXJnZXQgaW4gdGFyZ2V0cykgewogIGZvciAodmFyIGluIHZhcmlhYmxlcykgewogICAgCiAgICBmb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUodGFyZ2V0LCB2YXIsIHNlcCA9ICIgfiAiKSkKICAgIAogICAgIyBmaXQgdGhlIGxpbmVhciBtb2RlbAogICAgbW9kZWwgPC0gbG0oZm9ybXVsYSwgZGF0YSA9IGRhdGEyKQogICAgCiAgICBhZGp1c3RlZF9yMlt0YXJnZXQsIHZhcl0gPC0gc3VtbWFyeShtb2RlbCkkYWRqLnIuc3F1YXJlZAogIH0KfQoKIyBjb252ZXJ0IHRoZSBtYXRyaXggdG8gYSBkYXRhIGZyYW1lCmFkanVzdGVkX3IyX2RmIDwtIGFzLmRhdGEuZnJhbWUoYWRqdXN0ZWRfcjIpCgoKcHJpbnQoYWRqdXN0ZWRfcjJfZGYpCmBgYApDb25zaWRlcmluZyB0aGUgdGhyZWUgcG90ZW50aWFsIHRhcmdldCB2YXJpYWJsZXMsICdtaW5fdGVtcF9iaW5lX2plbmtzJyBjb25zaXN0ZW50bHkgc2hvd3MgYmV0dGVyIHBlcmZvcm1hbmNlIGluIHRlcm1zIG9mIFItc3F1YXJlZCB2YWx1ZXMuCgpUaHVzIGZhciwgd2UgaGF2ZSBjYXRlZ29yaXplZCBvdXIgZmVhdHVyZXMgaW50byBjb250aW51b3VzIGFuZCBjYXRlZ29yaWNhbCBjYW5kaWRhdGVzLgoKT3VyIGZpbmFsIGZlYXR1cmUgY2FuZGlkYXRlcywgYXMgZGV0ZXJtaW5lZCBieSB0aGVpciBjb2xsaW5lYXJpdHkgYW5kIGNvcnJlbGF0aW9uIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSwgYXJlIGFzIGZvbGxvd3M6CgpgYGB7cn0Kc2NhbGVkX3N1aWNpZGVfcmF0aW9fdmFyIDwtYygieWVhciIsImNvdW50cnkiLCJzZXgiLCJhZ2UiLCJjb250aW5lbnQiLCJwb3B1bGF0aW9uX2JpbmVfamVua3MiLCJzY2FsZWRfR0RQX2Zvcl95ZWFyIiwibWluX3RlbXBfYmluZV9qZW5rcyIsImdkcF9wZXJfY2FwaXRhX2JpbmVfamVua3MiLCJzY2FsZWRfbWluX3RlbXAiKQpzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW9fdmFyIDwtYygieWVhciIsImNvdW50cnkiLCJzZXgiLCJhZ2UiLCJjb250aW5lbnQiLCJwb3B1bGF0aW9uX2JpbmVfamVua3MiLCJzY2FsZWRfbG9nX0dEUF9jYXBpdGEiLCJtaW5fdGVtcF9iaW5lX2plbmtzIiwiZ2RwX3Blcl9jYXBpdGFfYmluZV9qZW5rcyIsInNjYWxlZF9hdmdfdGVtcCIpCnNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW9fdmFyIDwtYygieWVhciIsImNvdW50cnkiLCJzZXgiLCJhZ2UiLCJjb250aW5lbnQiLCJwb3B1bGF0aW9uX2JpbmVfamVua3MiLCJzY2FsZWRfbG9nX0dEUF9jYXBpdGEiICwibWluX3RlbXBfYmluZV9qZW5rcyIsImdkcF9wZXJfY2FwaXRhX2JpbmVfamVua3MiLCJzY2FsZWRfYXZnX3RlbXAiKQoKYGBgCiMjIDQuMyBNb2RlbHMgc2VsZWN0aW9uCnNvIGZvciB3ZSBoYXZlIDMgdGFyZ2V0IHZhcmlhYmxlIGFuZCBmb3IgZWFjaCBvbmUgd2UgZm91bmQgZGlmZmVyZW50IHByb3BlciB2YXJpYWJsZS4gaW4gdGhpcyBzZWN0aW9uIHdlIGluc3BlY3QgZGlmZmVyZW50IG1vZGVscyB3aXRoIGRpZmZlcmVudCBjcml0ZXJpYSAKClRvIGFwcGx5IGxpbmVhciByZWdyZXNzaW9uIHdlIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgZm91ciBjb25kaXRpb25zIGFyZSBzYXRpc2ZpZWQ6CgoxLk5vIG11bHRpY29sbGluZWFyaXR5OiBubyBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlczsKMi5MaW5lYXJpdHk6IHRoZXJlIG11c3QgYmUgYSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRhcmdldCB2YXJpYWJsZXNhbmQgdGhlIG90aGVyIHZhcmlhYmxlczsKMy5Ob3JtYWxpdHk6IHRoZSByZXNpZHVhbHMgbXVzdCBiZSBub3JtYWxseSBkaXN0cmlidXRlZDsKNC5Ib21vc2NlZGFzdGljaXR5OiB0aGUgcmVzaWR1YWxzIG11c3QgaGF2ZSBhIGNvbnN0YW50IHZhcmlhbmNlCgppbiBwcmV2aW91ZXMgc2VjdGlvbiB3ZSBpbnNwZWN0IG11bHRpbGluaWFyaXR5IHByb2JsZW0gYW5kIGdhdmUgcHJvcGVyIHNvbHV0aW9uIGZvciBlYWNoIHRhcmd0cwpsZXRzIGZpcnN0IG1ha2UgYSBzaW1wbGUgbW9kZWwgZm9yIGVhY2ggdmFyaWFibGUgYW5kIHNlZSB3aGljaCBjb25kaXRpb25zIHdpbGwgbWVldC4KYGBge3J9CiNzdWljaWRlX3JhdGlvCmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgic2NhbGVkX3N1aWNpZGVfcmF0aW8iLCAifiIsIHBhc3RlKHNjYWxlZF9zdWljaWRlX3JhdGlvX3ZhciwgY29sbGFwc2UgPSAiICsgIikpKQoKbW9kZWxfc3VpY2lkZV9yYXRpbyA8LSBsbShmb3JtdWxhLCBkYXRhID0gZGF0YTIpCnN1bW1hcnkobW9kZWxfc3VpY2lkZV9yYXRpbykKYGBgCgpgYGB7cn0KZGF0YTIgPC0gZGF0YTIlPiUKICBmaWx0ZXIoYWdlICE9ICc1LTE0JykKYGBgCgpgYGB7cn0KI2xvZ19zdWljaWRlX3JhdGlvCmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgic2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvIiwgIn4iLCBwYXN0ZShzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW9fdmFyLCBjb2xsYXBzZSA9ICIgKyAiKSkpCgptb2RlbF9sb2dfc3VpY2lkZV9yYXRpbyA8LSBsbShmb3JtdWxhLCBkYXRhID0gZGF0YTIpCnN1bW1hcnkobW9kZWxfbG9nX3N1aWNpZGVfcmF0aW8pCmBgYApgYGB7cn0KI3NzcXJ0X3VpY2lkZV9yYXRpbwpmb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUoInNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8iLCAifiIsIHBhc3RlKHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW9fdmFyLCBjb2xsYXBzZSA9ICIgKyAiKSkpCgptb2RlbF9zcXJ0X3N1aWNpZGVfcmF0aW8gPC0gbG0oZm9ybXVsYSwgZGF0YSA9IGRhdGEyKQpzdW1tYXJ5KG1vZGVsX3NxcnRfc3VpY2lkZV9yYXRpbykKYGBgCgpCZWZvcmUgd2UgZGVsdmUgZGVlcGVyIGludG8gb3VyIGxpbmVhciByZWdyZXNzaW9uIGFuYWx5c2lzLCBpdCdzIGNydWNpYWwgdG8gZW1waGFzaXplIHRoYXQgdGhlIGFzc3VtcHRpb25zIHVuZGVycGlubmluZyB0aGlzIG1vZGVsIGRvbid0IG5lZWQgdG8gYmUgZmxhd2xlc3NseSBtZXQuIEhvd2V2ZXIsIHNldmVyZSB2aW9sYXRpb25zIGNhbiBza2V3IHRoZSBtb2RlbCdzIGFjY3VyYWN5IGFuZCBsZWFkIHRvIG1pc2xlYWRpbmcgcmVzdWx0cy4KCk5vdywgbGV0J3MgdHVybiBvdXIgYXR0ZW50aW9uIHRvIGV2YWx1YXRpbmcgb3RoZXIgbmVjZXNzYXJ5IGNvbmRpdGlvbnMgZm9yIG91ciByZWdyZXNzaW9uIG1vZGVsLgoKYGBge3J9CnBsb3QobW9kZWxfc3VpY2lkZV9yYXRpbywxKQpgYGAKYGBge3J9CnBsb3QobW9kZWxfbG9nX3N1aWNpZGVfcmF0aW8sMSkKYGBgCmBgYHtyfQpwbG90KG1vZGVsX3NxcnRfc3VpY2lkZV9yYXRpbywxKQpgYGAKClRoZSBsaW5lYXJpdHkgY29uZGl0aW9uIGRvZXNuJ3QgYXBwZWFyIHRvIGJlIHBlcmZlY3RseSBzYXRpc2ZpZWQgZm9yIGFueSBvZiBvdXIgdGFyZ2V0cy4gSG93ZXZlciwgdGhlIHJlc2lkdWFscyBmb3IgdGhlIGxvZy10cmFuc2Zvcm1lZCB0YXJnZXQgYXJlIHJlYXNvbmFibHkgd2VsbC1kaXN0cmlidXRlZCBhbmQgZG8gbm90IGRlbW9uc3RyYXRlIGFueSBkaXNjZXJuaWJsZSBwYXR0ZXJucy4gT24gdGhlIG90aGVyIGhhbmQsIHRoZSBzdWljaWRlX3JhdGlvIGFuZCBzcXJ0X3N1aWNpZGVfcmF0aW8gdGFyZ2V0cywgcGFydGljdWxhcmx5IHRoZSBmb3JtZXIsIGRvIGRpc3BsYXkgdW51c3VhbCBwYXR0ZXJucyBpbiB0aGUgcmVzaWR1YWxzIGdyYXBoLiBMZXQncyBjb250aW51ZSBvdXIgZXZhbHVhdGlvbiBieSBhc3Nlc3NpbmcgdGhlIG5leHQgYXNzdW1wdGlvbiBvZiBvdXIgbW9kZWwuCgoKCmBgYHtyfQpwbG90KG1vZGVsX3N1aWNpZGVfcmF0aW8sMikKYGBgCmBgYHtyfQpwbG90KG1vZGVsX2xvZ19zdWljaWRlX3JhdGlvLDIpCmBgYApgYGB7cn0KcGxvdChtb2RlbF9zcXJ0X3N1aWNpZGVfcmF0aW8sMikKYGBgCmBgYHtyfQpsaWJyYXJ5KG9sc3JyKQpvbHNfcGxvdF9yZXNpZF9oaXN0KG1vZGVsX3N1aWNpZGVfcmF0aW8pCm9sc19wbG90X3Jlc2lkX2hpc3QobW9kZWxfbG9nX3N1aWNpZGVfcmF0aW8pCm9sc19wbG90X3Jlc2lkX2hpc3QobW9kZWxfc3FydF9zdWljaWRlX3JhdGlvKQpgYGAKQXMgb2JzZXJ2ZWQsIHRoZSBtYWpvcml0eSBvZiBvdXIgcmVzaWR1YWxzIGZvciB0aGUgbG9nX3N1aWNpZGVfcmF0aW8gYW5kIHNxcnRfc3VpY2lkZV9yYXRpbyBtb2RlbHMgbGllIHdpdGhpbiAtMSBhbmQgMSwgYW5kIC0xLjUgYW5kIDEuNSByZXNwZWN0aXZlbHksIGFuZCB0aGVpciBkaXN0cmlidXRpb25zIGxhcmdlbHkgZm9sbG93IGEgbm9ybWFsIHBhdHRlcm4uIEhvd2V2ZXIsIHRoZSBzdWljaWRlX3JhdGlvIG1vZGVsIGRvZXNuJ3QgYXBwZWFyIHRvIHNhdGlzZnkgdGhlc2UgY29uZGl0aW9ucyBhcyBlZmZlY3RpdmVseS4KClRvIGV2YWx1YXRlIHRoZSBmaW5hbCBhc3N1bXB0aW9uIC0gaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgLSB3ZSBhcHBseSB0aGUgQnJldXNjaC1QYWdhbiB0ZXN0LiBUaGUgdGVzdCdzIG51bGwgaHlwb3RoZXNpcyBhc3N1bWVzIGhvbW9zY2VkYXN0aWNpdHkuIElmIHRoZSBwLXZhbHVlIGlzIHNpZ25pZmljYW50IChnZW5lcmFsbHksIGxlc3MgdGhhbiAwLjA1KSwgaXQgc3VnZ2VzdHMgYSBkZXZpYXRpb24gZnJvbSB0aGlzIGFzc3VtcHRpb24sIGluZGljYXRpbmcgaGV0ZXJvc2NlZGFzdGljaXR5LgogCmBgYHtyfQpsaWJyYXJ5KGxtdGVzdCkKYnB0ZXN0KG1vZGVsX3N1aWNpZGVfcmF0aW8pCmJwdGVzdChtb2RlbF9sb2dfc3VpY2lkZV9yYXRpbykKYnB0ZXN0KG1vZGVsX3NxcnRfc3VpY2lkZV9yYXRpbykKYGBgCgpgYGB7cn0KcGxvdChtb2RlbF9sb2dfc3VpY2lkZV9yYXRpbywzKQpwbG90KG1vZGVsX3NxcnRfc3VpY2lkZV9yYXRpbywzKQpgYGAKVGhlIHBsb3QgdmlzdWFsaXplcyB0aGUgcmVzaWR1YWxzJyB2YXJpYW5jZSBpbiByZWxhdGlvbiB0byB0aGUgcHJlZGljdG9ycy4gSWRlYWxseSwgdGhlIHJlc2lkdWFscyBzaG91bGQgYmUgcmFuZG9tbHkgc2NhdHRlcmVkIGFyb3VuZCB0aGUgY2VudGVybGluZSwgc2lnbmlmeWluZyBob21vc2NlZGFzdGljaXR5LgoKSW4gb3VyIGNhc2UsIHJlc2lkdWFscyBhcmUgc29tZXdoYXQgZXZlbmx5IGRpc3RyaWJ1dGVkLCBpbmRpY2F0aW5nIHZpb2xhdGlvbiBvZiBob21vc2NlZGFzdGljaXR5LiBUaGlzIHN1Z2dlc3RzIG91ciBtb2RlbCBwcm9iYWJpeSBiZSAgbGVzcyBhY2N1cmF0ZSBhY3Jvc3MgdGhlIHByZWRpY3RvciByYW5nZSwgYnV0IGl0IGRvZXMgbm90IGRyYXN0aWNhbGx5IGltcGFjdCB0aGUgb3ZlcmFsbCBtb2RlbCdzIHJlbGlhYmlsaXR5Lgp3ZSBjYW4gY29uY2x1ZGUgdGhhdCBzdWljaWRlX3JhdGUgbm90IGEgZ29vZCBjYW5kaWRhdGUgZm9yIHRhcmdldCB2YWx1ZS4KCiMjIyA0LjMuMSBGZWF0dXJlIHNlbGVjdGlvbiAKRmVhdHVyZSBzZWxlY3Rpb24sIGFsc28ga25vd24gYXMgdmFyaWFibGUgc2VsZWN0aW9uLCBhdHRyaWJ1dGUgc2VsZWN0aW9uLCBvciB2YXJpYWJsZSBzdWJzZXQgc2VsZWN0aW9uLCBpcyB0aGUgcHJvY2VzcyBvZiBzZWxlY3RpbmcgYSBzdWJzZXQgb2YgcmVsZXZhbnQgZmVhdHVyZXMgZm9yIHVzZSBpbiBtb2RlbCBjb25zdHJ1Y3Rpb24uIFRoZSBnb2FsIG9mIGZlYXR1cmUgc2VsZWN0aW9uIGlzIHRocmVlLWZvbGQ6CgoqSW1wcm92aW5nIE1vZGVsIFBlcmZvcm1hbmNlOiBXaGVuIGlycmVsZXZhbnQgb3IgcGFydGlhbGx5IHJlbGV2YW50IGZlYXR1cmVzIGFyZSB1c2VkIHRvIGNvbnN0cnVjdCBhIHByZWRpY3RpdmUgbW9kZWwsIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgY2FuIGJlIHNpZ25pZmljYW50bHkgZGVncmFkZWQuIEJ5IHNlbGVjdGluZyBvbmx5IHRoZSBtb3N0IHJlbGV2YW50IGZlYXR1cmVzIHRvIHVzZSBpbiBtb2RlbCBjb25zdHJ1Y3Rpb24sIHdlIGNhbiBlbmhhbmNlIHRoZSBwcmVkaWN0aXZlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbC4KCipSZWR1Y2luZyBPdmVyZml0dGluZzogVG9vIG1hbnkgZmVhdHVyZXMgaW4gdGhlIG1vZGVsIGNhbiBsZWFkIHRvIG92ZXJmaXR0aW5nLCB3aGVyZSB0aGUgbW9kZWwgcGVyZm9ybXMgd2VsbCBvbiB0aGUgdHJhaW5pbmcgZGF0YSBidXQgcG9vcmx5IG9uIHVuc2VlbiBkYXRhLiBCeSByZWR1Y2luZyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzLCB3ZSBjYW4gbWFrZSB0aGUgbW9kZWwgbW9yZSBnZW5lcmFsaXphYmxlLgoKKkVuaGFuY2luZyBJbnRlcnByZXRhYmlsaXR5OiBNb2RlbHMgd2l0aCBmZXdlciBmZWF0dXJlcyBhcmUgc2ltcGxlciBhbmQgZWFzaWVyIHRvIGludGVycHJldC4KClJlZHVjaW5nIFRyYWluaW5nIFRpbWU6IEZld2VyIGZlYXR1cmVzIG1lYW4gZmFzdGVyIHRyYWluaW5nIHRpbWVzLgoKRmVhdHVyZSBzZWxlY3Rpb24gbWV0aG9kcyBhcmUgaW50ZW5kZWQgdG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgaW5wdXQgdmFyaWFibGVzIHRvIHRob3NlIHRoYXQgYXJlIGJlbGlldmVkIHRvIGJlIG1vc3QgdXNlZnVsIHRvIGEgbW9kZWwgaW4gb3JkZXIgdG8gcHJlZGljdCB0aGUgdGFyZ2V0IHZhcmlhYmxlLiBOb3QgYWxsIGZlYXR1cmVzIGFyZSBjcmVhdGVkIGVxdWFsLiBTb21lIGFyZSByZWxldmFudCB0byB0aGUgdGFyZ2V0IHZhcmlhYmxlLCBzb21lIGFyZSBpcnJlbGV2YW50LCBhbmQgc29tZSBhcmUgcmVkdW5kYW50LiBGZWF0dXJlIHNlbGVjdGlvbiBlbmFibGVzIHVzIHRvIGZvY3VzIG9uIHRoZSByZWxldmFudCBhbmQgbm9uLXJlZHVuZGFudCBmZWF0dXJlcywgaW5jcmVhc2luZyBvdXIgbW9kZWwncyBwZXJmb3JtYW5jZSBhbmQgaW50ZXJwcmV0YWJpbGl0eS4gCgpGZWF0dXJlIHNlbGVjdGlvbiBtZXRob2RzOgoKRm9yd2FyZCBTZWxlY3Rpb246IFlvdSBzdGFydCB3aXRoIGFuIGVtcHR5IG1vZGVsIGFuZCBhZGQgcHJlZGljdG9ycyBvbmUgYnkgb25lLiBJbiBlYWNoIHN0ZXAsIHlvdSBhZGQgdGhlIHZhcmlhYmxlIHRoYXQgZ2l2ZXMgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnQgdG8gdGhlIG1vZGVsLgoKQmFja3dhcmQgU2VsZWN0aW9uOiBZb3Ugc3RhcnQgd2l0aCB0aGUgZnVsbCBtb2RlbCBhbmQgcmVtb3ZlIHByZWRpY3RvcnMgb25lIGJ5IG9uZS4gSW4gZWFjaCBzdGVwLCB5b3UgcmVtb3ZlIHRoZSB2YXJpYWJsZSB0aGF0IGlzIHRoZSBsZWFzdCBzaWduaWZpY2FudC4KCk1peGVkIFNlbGVjdGlvbjogVGhpcyBpcyBhIGNvbWJpbmF0aW9uIG9mIGZvcndhcmQgYW5kIGJhY2t3YXJkIHNlbGVjdGlvbi4gWW91IHN0YXJ0IHdpdGggYW4gZW1wdHkgbW9kZWwsIGFkZCB2YXJpYWJsZXMgYXMgaW4gZm9yd2FyZCBzZWxlY3Rpb24sIGJ1dCBhZnRlciBhZGRpbmcgZWFjaCBuZXcgdmFyaWFibGUsIHRoZSBtZXRob2QgbWF5IGFsc28gcmVtb3ZlIHZhcmlhYmxlcyB0aGF0IGRvIG5vdCBjb250cmlidXRlIHRvIHRoZSBtb2RlbCBmaXQuCmluIHRoaXMgcHJvamVjdCB3ZSB1c2UgY3JpdGVyaWEgbGlrZSBSU1MsIGFkanIyLCBNYWxsb3figJlzIENwIChjcCkgYW5kIEJheWVzaWFuIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQklDKS4KUmVzaWR1YWwgU3VtIG9mIFNxdWFyZXMgKFJTUyk6IFRoaXMgaXMgYSBtZWFzdXJlIG9mIHRoZSBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSBkYXRhIGFuZCBhbiBlc3RpbWF0aW9uIG1vZGVsLiBBIHNtYWxsIFJTUyBpbmRpY2F0ZXMgYSB0aWdodCBmaXQgb2YgdGhlIG1vZGVsIHRvIHRoZSBkYXRhLgoKQWRqdXN0ZWQgUi1zcXVhcmVkIChhZGpyMik6IEl0IGlzIGEgbW9kaWZpY2F0aW9uIG9mIFItc3F1YXJlZCB0aGF0IGFkanVzdHMgZm9yIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwuIFVubGlrZSBSLXNxdWFyZWQsIHRoZSBhZGp1c3RlZCBSLXNxdWFyZWQgaW5jcmVhc2VzIG9ubHkgaWYgdGhlIG5ldyB0ZXJtIGVuaGFuY2VzIHRoZSBtb2RlbCBtb3JlIHRoYW4gd291bGQgYmUgZXhwZWN0ZWQgYnkgY2hhbmNlLgoKTWFsbG934oCZcyBDcCAoY3ApOiBUaGlzIGNyaXRlcmlvbiBhdHRlbXB0cyB0byBpZGVudGlmeSBhIG1vZGVsIHdpdGggYSBiYWxhbmNlIGJldHdlZW4gdW5kZXItZml0dGluZyBhbmQgb3Zlci1maXR0aW5nLiBJdHMgaWRlYWwgdmFsdWUgaXMgcCAodGhlIG51bWJlciBvZiBwcmVkaWN0b3JzIGluIHRoZSBtb2RlbCksIGFuZCBhIGdvb2QgbW9kZWwgaXMgYSBtb2RlbCB3aGVyZSBDcCBpcyBuZWFybHkgZXF1YWwgdG8gaXRzIHAtdmFsdWUuCgpCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEJJQyk6IFRoaXMgY3JpdGVyaW9uIGRlYWxzIHdpdGggbW9kZWwgc2VsZWN0aW9uIHByb2JsZW1zLiBMb3dlciBCSUMgbWVhbnMgYmV0dGVyIG1vZGVsLgoKCmxvZ19zdWljaWRlX3JhdGlvCmBgYHtyfQpsaWJyYXJ5KGxlYXBzKQoKIyBGaXJzdCwgZml0IGEgZnVsbCBtb2RlbApmdWxsX21vZGVsIDwtIG1vZGVsX2xvZ19zdWljaWRlX3JhdGlvCgojbG9nX3N1aWNpZGVfcmF0aW8KZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8iLCAifiIsIHBhc3RlKHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpb192YXIsIGNvbGxhcHNlID0gIiArICIpKSkKCgoKIyBGb3J3YXJkIFNlbGVjdGlvbgpmb3J3YXJkX21vZGVsX2xvZyA8LSByZWdzdWJzZXRzKGZvcm11bGEsIGRhdGEgPSBkYXRhMiwgbnZtYXggPSBsZW5ndGgoZGF0YTIpLTEsIG1ldGhvZCA9ICJmb3J3YXJkIikKZm9yd2FyZF9zdW1tYXJ5IDwtIHN1bW1hcnkoZm9yd2FyZF9tb2RlbF9sb2cpCgojIEJhY2t3YXJkIFNlbGVjdGlvbgpiYWNrd2FyZF9tb2RlbF9sb2cgPC0gcmVnc3Vic2V0cyhmb3JtdWxhLCBkYXRhID0gZGF0YTIsIG52bWF4ID0gbGVuZ3RoKGRhdGEyKS0xLCBtZXRob2QgPSAiYmFja3dhcmQiKQpiYWNrd2FyZF9zdW1tYXJ5IDwtIHN1bW1hcnkoYmFja3dhcmRfbW9kZWxfbG9nKQoKIyBNaXhlZCAoc3RlcHdpc2UpIHNlbGVjdGlvbgpzdGVwd2lzZV9tb2RlbF9sb2cgPC0gcmVnc3Vic2V0cyhmb3JtdWxhLCBkYXRhID0gZGF0YTIsIG52bWF4ID0gbGVuZ3RoKGRhdGEyKS0xLCBtZXRob2QgPSAic2VxcmVwIikKc3RlcHdpc2Vfc3VtbWFyeSA8LSBzdW1tYXJ5KHN0ZXB3aXNlX21vZGVsX2xvZykKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggdGhlIGNyaXRlcmlhIGZvciBlYWNoIG1ldGhvZApjb21wYXJpc29uX2RmX2xvZyA8LSBkYXRhLmZyYW1lKAogIE1ldGhvZCA9IGMoIkZvcndhcmQiLCAiQmFja3dhcmQiLCAiTWl4ZWQiKSwKICBSU1MgPSBjKGZvcndhcmRfc3VtbWFyeSRyc3Nbd2hpY2gubWluKGZvcndhcmRfc3VtbWFyeSRjcCldLCAKICAgICAgICAgIGJhY2t3YXJkX3N1bW1hcnkkcnNzW3doaWNoLm1pbihiYWNrd2FyZF9zdW1tYXJ5JGNwKV0sIAogICAgICAgICAgc3RlcHdpc2Vfc3VtbWFyeSRyc3Nbd2hpY2gubWluKHN0ZXB3aXNlX3N1bW1hcnkkY3ApXSksCiAgQWRqdXN0ZWRSMiA9IGMobWF4KGZvcndhcmRfc3VtbWFyeSRhZGpyMiksIG1heChiYWNrd2FyZF9zdW1tYXJ5JGFkanIyKSwgbWF4KHN0ZXB3aXNlX3N1bW1hcnkkYWRqcjIpKSwKICBDcCA9IGMobWluKGZvcndhcmRfc3VtbWFyeSRjcCksIG1pbihiYWNrd2FyZF9zdW1tYXJ5JGNwKSwgbWluKHN0ZXB3aXNlX3N1bW1hcnkkY3ApKSwKICBCSUMgPSBjKG1pbihmb3J3YXJkX3N1bW1hcnkkYmljKSwgbWluKGJhY2t3YXJkX3N1bW1hcnkkYmljKSwgbWluKHN0ZXB3aXNlX3N1bW1hcnkkYmljKSkKKQoKCnByaW50KGNvbXBhcmlzb25fZGZfbG9nKQoKYGBgCmZvcndhcmQgaGFzIGhpZ2hlciBhZGp1c3RlZFIyIGFuZCBsb3dlciBCSUMgZm9yIGxvZy4KCmBgYHtyfQpsaWJyYXJ5KGxlYXBzKQoKIyBGaXJzdCwgZml0IGEgZnVsbCBtb2RlbApmdWxsX21vZGVsIDwtIG1vZGVsX3NxcnRfc3VpY2lkZV9yYXRpbwoKI2xvZ19zdWljaWRlX3JhdGlvCmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgic2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpbyIsICJ+IiwgcGFzdGUoc2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpb192YXIsIGNvbGxhcHNlID0gIiArICIpKSkKCiAgCgojIEZvcndhcmQgU2VsZWN0aW9uCmZvcndhcmRfbW9kZWxfc3FydCA8LSByZWdzdWJzZXRzKGZvcm11bGEsIGRhdGEgPSBkYXRhMiwgbnZtYXggPSBsZW5ndGgoZGF0YTIpLTEsIG1ldGhvZCA9ICJmb3J3YXJkIikKZm9yd2FyZF9zdW1tYXJ5IDwtIHN1bW1hcnkoZm9yd2FyZF9tb2RlbF9zcXJ0KQoKIyBCYWNrd2FyZCBTZWxlY3Rpb24KYmFja3dhcmRfbW9kZWxfc3FydCA8LSByZWdzdWJzZXRzKGZvcm11bGEsIGRhdGEgPSBkYXRhMiwgbnZtYXggPSBsZW5ndGgoZGF0YTIpLTEsIG1ldGhvZCA9ICJiYWNrd2FyZCIpCgpiYWNrd2FyZF9zdW1tYXJ5IDwtIHN1bW1hcnkoYmFja3dhcmRfbW9kZWxfc3FydCkKCiMgTWl4ZWQgKHN0ZXB3aXNlKSBzZWxlY3Rpb24Kc3RlcHdpc2VfbW9kZWxfc3FydCA8LSByZWdzdWJzZXRzKGZvcm11bGEsIGRhdGEgPSBkYXRhMiwgbnZtYXggPSBsZW5ndGgoZGF0YTIpLTEsIG1ldGhvZCA9ICJzZXFyZXAiKQpzdGVwd2lzZV9zdW1tYXJ5IDwtIHN1bW1hcnkoc3RlcHdpc2VfbW9kZWxfc3FydCkKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggdGhlIGNyaXRlcmlhIGZvciBlYWNoIG1ldGhvZApjb21wYXJpc29uX2RmX3NxcnQgPC0gZGF0YS5mcmFtZSgKICBNZXRob2QgPSBjKCJGb3J3YXJkIiwgIkJhY2t3YXJkIiwgIk1peGVkIiksCiAgUlNTID0gYyhmb3J3YXJkX3N1bW1hcnkkcnNzW3doaWNoLm1pbihmb3J3YXJkX3N1bW1hcnkkY3ApXSwgCiAgICAgICAgICBiYWNrd2FyZF9zdW1tYXJ5JHJzc1t3aGljaC5taW4oYmFja3dhcmRfc3VtbWFyeSRjcCldLCAKICAgICAgICAgIHN0ZXB3aXNlX3N1bW1hcnkkcnNzW3doaWNoLm1pbihzdGVwd2lzZV9zdW1tYXJ5JGNwKV0pLAogIEFkanVzdGVkUjIgPSBjKG1heChmb3J3YXJkX3N1bW1hcnkkYWRqcjIpLCBtYXgoYmFja3dhcmRfc3VtbWFyeSRhZGpyMiksIG1heChzdGVwd2lzZV9zdW1tYXJ5JGFkanIyKSksCiAgQ3AgPSBjKG1pbihmb3J3YXJkX3N1bW1hcnkkY3ApLCBtaW4oYmFja3dhcmRfc3VtbWFyeSRjcCksIG1pbihzdGVwd2lzZV9zdW1tYXJ5JGNwKSksCiAgQklDID0gYyhtaW4oZm9yd2FyZF9zdW1tYXJ5JGJpYyksIG1pbihiYWNrd2FyZF9zdW1tYXJ5JGJpYyksIG1pbihzdGVwd2lzZV9zdW1tYXJ5JGJpYykpCikKCgpwcmludChjb21wYXJpc29uX2RmX3NxcnQpCgpgYGAKCgpmb3J3YXJkIGhhcyBoaWdoZXIgYWRqdXN0ZWRSMiBhbmQgbG93ZXIgQklDIGZvciBib3RoIGxvZyBhbmQgc3FydCBidXQgaXRzIG11Y2ggbGVzc2VyIHRoYW4gb3JpZ2luYWwgbW9kZWwKCmBgYHtyfQp2YXIxPC1jKHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpb192YXIsInNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbyIpCmRhdGEzPC1kYXRhMiAlPiUgc2VsZWN0KG9uZV9vZih2YXIxKSkKdHJhaW5EYXRhX2xvZyA8LSBkYXRhMyAlPiUgZmlsdGVyKHllYXIgPD0yMDEwKQp0ZXN0RGF0YV9sb2cgPC0gZGF0YTMgJT4lIGZpbHRlcih5ZWFyID4yMDEwKQpgYGAKCmBgYHtyfQp2YXIxPC1jKHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW9fdmFyLCJzY2FsZWRfc3FydF9zdWljaWRlX3JhdGlvIikKZGF0YTM8LWRhdGEyICU+JSBzZWxlY3Qob25lX29mKHZhcjEpKQp0cmFpbkRhdGFfc3FydCA8LSBkYXRhMyAlPiUgZmlsdGVyKHllYXIgPD0yMDEwKQp0ZXN0RGF0YV9zcXJ0IDwtIGRhdGEzICU+JSBmaWx0ZXIoeWVhciA+MjAxMCkKYGBgCmp1c3QgZm9yIHNlZWluZyBob3cgbXVjaCBvdXIgd29ya3Mgb24gZGF0YSBpcyBiZWVuIGluZmx1ZW50aWFsIG9uIHBlcmZvcm1hbmNlIG9mIG1vZGVsIHdlIGluc3BlY3QgaW5pdGlhbCBkYXRhIHBlcmZvcm1hbmNlIG9uIG1vZGVscyB0b28KYGBge3J9CmluaXRpYWxfdmFyPC1jKCJjb3VudHJ5IiwieWVhciIsInNleCIsImFnZSIsInN1aWNpZGVfcmF0aW8iLCJHRFBfZm9yX3llYXIiLCJHRFBfcGVyX2NhcGl0YSIsImdlbmVyYXRpb24iLCJjb250aW5lbnQiLCJsaWZlX2V4cCIsImF2Z190ZW1wIiwibWF4X3RlbXAiLCJtaW5fdGVtcCIpCmRhdGEzPC1kYXRhICU+JSBzZWxlY3Qob25lX29mKGluaXRpYWxfdmFyKSkKdHJhaW5EYXRhX2luaXRpYWwgPC0gZGF0YTMgJT4lIGZpbHRlcih5ZWFyIDw9MjAxMCkKdGVzdERhdGFfaW5pdGlhbCA8LSBkYXRhMyAlPiUgZmlsdGVyKHllYXIgPjIwMTApCmBgYAojIyMgNC40LjEgU2ltcGxlIGxpbmVhciBtb2RlbCAKZmlyc3Qgd2UgdHJhaW4gYSBtb2RlbCBmb3IgdGFyZ2V0IGxvZyAKYGBge3J9CmxpYnJhcnkoTWV0cmljcykKZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8iLCAifiIsIHBhc3RlKHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpb192YXIsIGNvbGxhcHNlID0gIiArICIpKSkKCm1vZGVsIDwtIGxtKGZvcm11bGEsIGRhdGEgPSB0cmFpbkRhdGFfbG9nKQpkYXRhX3Rlc3QgPC0gdGVzdERhdGFfbG9nCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdGluZyBkYXRhCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YV9sb2cpCnRhcmdldCA8LSJzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8iCiMgQ2FsY3VsYXRlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKQptc2UgPC0gbXNlKHRlc3REYXRhX2xvZyRzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8sIHByZWRpY3Rpb25zKQpwcmludChwYXN0ZTAoIk1TRTogIiwgbXNlKSkKCiMgQ2FsY3VsYXRlIFItc3F1YXJlZApzc2UgPSBzdW0oKHByZWRpY3Rpb25zIC0gdGVzdERhdGFfbG9nJHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbyleMikKc3N0ID0gc3VtKCh0ZXN0RGF0YV9sb2ckc2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvIC0gbWVhbih0ZXN0RGF0YV9sb2ckc2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvKSleMikKcl9zcXVhcmVkID0gMSAtIHNzZSAvIHNzdApwcmludChwYXN0ZTAoIlItc3F1YXJlZDogIiwgcl9zcXVhcmVkKSkKCiMgQ2FsY3VsYXRlIEFkanVzdGVkIFItc3F1YXJlZApuID0gbGVuZ3RoKHRlc3REYXRhX2xvZyRzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8pICMgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwpwID0gbGVuZ3RoKGNvZWYobW9kZWwpKSAtIDEgIyBudW1iZXIgb2YgcHJlZGljdG9ycwphZGp1c3RlZF9yX3NxdWFyZWQgPSAxIC0gKDEgLSByX3NxdWFyZWQpICogKChuIC0gMSkgLyAobiAtIHAgLSAxKSkKcHJpbnQocGFzdGUwKCJBZGp1c3RlZCBSLXNxdWFyZWQ6ICIsIGFkanVzdGVkX3Jfc3F1YXJlZCkpCgpgYGAKCgpgYGB7cn0KbGlicmFyeShNZXRyaWNzKQpmb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUoInNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8iLCAifiIsIHBhc3RlKHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW9fdmFyLCBjb2xsYXBzZSA9ICIgKyAiKSkpCgptb2RlbCA8LSBsbShmb3JtdWxhLCBkYXRhID0gdHJhaW5EYXRhX3NxcnQpCgojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3RpbmcgZGF0YQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gdGVzdERhdGFfc3FydCkKCiMgQ2FsY3VsYXRlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKQptc2UgPC0gbXNlKHRlc3REYXRhX3NxcnQkc2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpbywgcHJlZGljdGlvbnMpCnByaW50KHBhc3RlMCgiTVNFOiAiLCBtc2UpKQoKIyBDYWxjdWxhdGUgUi1zcXVhcmVkCnNzZSA9IHN1bSgocHJlZGljdGlvbnMgLSB0ZXN0RGF0YV9zcXJ0JHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8pXjIpCnNzdCA9IHN1bSgodGVzdERhdGFfc3FydCRzY2FsZWRfc3FydF9zdWljaWRlX3JhdGlvIC0gbWVhbih0ZXN0RGF0YV9zcXJ0JHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8pKV4yKQpyX3NxdWFyZWQgPSAxIC0gc3NlIC8gc3N0CnByaW50KHBhc3RlMCgiUi1zcXVhcmVkOiAiLCByX3NxdWFyZWQpKQoKIyBDYWxjdWxhdGUgQWRqdXN0ZWQgUi1zcXVhcmVkCm4gPSBsZW5ndGgodGVzdERhdGFfc3FydCRzY2FsZWRfc3FydF9zdWljaWRlX3JhdGlvKSAjIG51bWJlciBvZiBvYnNlcnZhdGlvbnMKcCA9IGxlbmd0aChjb2VmKG1vZGVsKSkgLSAxICMgbnVtYmVyIG9mIHByZWRpY3RvcnMKYWRqdXN0ZWRfcl9zcXVhcmVkID0gMSAtICgxIC0gcl9zcXVhcmVkKSAqICgobiAtIDEpIC8gKG4gLSBwIC0gMSkpCnByaW50KHBhc3RlMCgiQWRqdXN0ZWQgUi1zcXVhcmVkOiAiLCBhZGp1c3RlZF9yX3NxdWFyZWQpKQoKYGBgCgppbml0aWFsX2RhdGEKYGBge3J9CmxpYnJhcnkoTWV0cmljcykKYSA8LWMoImNvdW50cnkiLCJ5ZWFyIiwic2V4IiwiYWdlIiwiR0RQX2Zvcl95ZWFyIiwiR0RQX3Blcl9jYXBpdGEiLCJnZW5lcmF0aW9uIiwiY29udGluZW50IiwibGlmZV9leHAiLCJhdmdfdGVtcCIsIm1heF90ZW1wIiwibWluX3RlbXAiKQpmb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUoInN1aWNpZGVfcmF0aW8iLCAifiIsIHBhc3RlKGEsIGNvbGxhcHNlID0gIiArICIpKSkKCm1vZGVsIDwtIGxtKGZvcm11bGEsIGRhdGEgPSB0cmFpbkRhdGFfaW5pdGlhbCkKCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdGluZyBkYXRhCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YV9pbml0aWFsKQoKIyBDYWxjdWxhdGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpCm1zZSA8LSBtc2UodGVzdERhdGFfaW5pdGlhbCRzdWljaWRlX3JhdGlvLCBwcmVkaWN0aW9ucykKcHJpbnQocGFzdGUwKCJNU0U6ICIsIG1zZSkpCgojIENhbGN1bGF0ZSBSLXNxdWFyZWQKc3NlID0gc3VtKChwcmVkaWN0aW9ucyAtIHRlc3REYXRhX2luaXRpYWwkc3VpY2lkZV9yYXRpbyleMikKc3N0ID0gc3VtKCh0ZXN0RGF0YV9pbml0aWFsJHN1aWNpZGVfcmF0aW8gLSBtZWFuKHRlc3REYXRhX2luaXRpYWwkc3VpY2lkZV9yYXRpbykpXjIpCnJfc3F1YXJlZCA9IDEgLSBzc2UgLyBzc3QKcHJpbnQocGFzdGUwKCJSLXNxdWFyZWQ6ICIsIHJfc3F1YXJlZCkpCgojIENhbGN1bGF0ZSBBZGp1c3RlZCBSLXNxdWFyZWQKbiA9IGxlbmd0aCh0ZXN0RGF0YV9pbml0aWFsJHN1aWNpZGVfcmF0aW8pICMgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwpwID0gbGVuZ3RoKGNvZWYobW9kZWwpKSAtIDEgIyBudW1iZXIgb2YgcHJlZGljdG9ycwphZGp1c3RlZF9yX3NxdWFyZWQgPSAxIC0gKDEgLSByX3NxdWFyZWQpICogKChuIC0gMSkgLyAobiAtIHAgLSAxKSkKcHJpbnQocGFzdGUwKCJBZGp1c3RlZCBSLXNxdWFyZWQ6ICIsIGFkanVzdGVkX3Jfc3F1YXJlZCkpCgpgYGAKCml0IHNob3dzIHRoYXQgb3VyIGhhcmQgd29yayB3YXMgZWZmZWN0aXZlCiMjIyA0LjQuMiBMYXNzbyByZWdyZXNzaW9uIG1vZGVsCkxhc3NvIHJlZ3Jlc3Npb24gaXMgYSB0eXBlIG9mIHJlZ3Jlc3Npb24gYW5hbHlzaXMgbWV0aG9kIHRoYXQgcGVyZm9ybXMgYm90aCB2YXJpYWJsZSBzZWxlY3Rpb24gYW5kIHJlZ3VsYXJpemF0aW9uIGluIG9yZGVyIHRvIGVuaGFuY2UgdGhlIHByZWRpY3Rpb24gYWNjdXJhY3kgYW5kIGludGVycHJldGFiaWxpdHkgb2YgdGhlIHN0YXRpc3RpY2FsIG1vZGVsIGl0IHByb2R1Y2VzLiBUaGUgdGVybSBMYXNzbyBpcyBhbiBhY3JvbnltIGZvciBMZWFzdCBBYnNvbHV0ZSBTaHJpbmthZ2UgYW5kIFNlbGVjdGlvbiBPcGVyYXRvci4KClRoZSBMYXNzbyBtZXRob2QgaW50cm9kdWNlcyBhIHBlbmFsdHkgdGVybSB0byB0aGUgbG9zcyBmdW5jdGlvbiBvZiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdGhhdCBpcyB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIG1hZ25pdHVkZSBvZiB0aGUgY29lZmZpY2llbnQgdmFsdWVzLCBvciBzaW1wbHkgdGhlIGFic29sdXRlIHZhbHVlIG9mIGVhY2ggY29lZmZpY2llbnQuCmxhc3NvIGZvciBsb2cgdGFyZ2V0CmBgYHtyfQoKbGlicmFyeShjYXJldCkKbGlicmFyeShnbG1uZXQpCgoKeF90cmFpbiA8LSBtb2RlbC5tYXRyaXgoc2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvfi4sIHRyYWluRGF0YV9sb2cpWywtMV0gIyBFeGNsdWRlIGludGVyY2VwdCBjb2x1bW4KeV90cmFpbiA8LSB0cmFpbkRhdGFfbG9nJHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbwp4X3Rlc3QgPC0gbW9kZWwubWF0cml4KHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpb34uLCB0ZXN0RGF0YV9sb2cpWywtMV0gIyBFeGNsdWRlIGludGVyY2VwdCBjb2x1bW4KeV90ZXN0IDwtIHRlc3REYXRhX2xvZyRzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8KCiMgRGVmaW5lIHRoZSBjcm9zcy12YWxpZGF0aW9uIGV4cGVyaW1lbnQKY3ZmaXQgPC0gY3YuZ2xtbmV0KHhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSwgdHlwZS5tZWFzdXJlID0gIm1zZSIpCgojIEdldCB0aGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUKbGFtYmRhX29wdGltYWwgPC0gY3ZmaXQkbGFtYmRhLm1pbgoKIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgdXNpbmcgdGhlIG9wdGltYWwgbGFtYmRhCmZpbmFsX21vZGVsIDwtIGdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDEsIGxhbWJkYSA9IGxhbWJkYV9vcHRpbWFsKQoKCnByZWRpY3Rpb25zIDwtIHByZWRpY3QoZmluYWxfbW9kZWwsIHMgPSBsYW1iZGFfb3B0aW1hbCwgbmV3eCA9IHhfdGVzdCkKCiMgRXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlCm1zZSA8LSBtZWFuKChwcmVkaWN0aW9ucyAtIHlfdGVzdCleMikKCnByaW50KHBhc3RlKCJNU0Ugb24gdGhlIHRlc3Qgc2V0OiAiLCBtc2UpKQoKcnNxIDwtIDEgLSBzdW0oKHByZWRpY3Rpb25zIC0geV90ZXN0KV4yKSAvIHN1bSgobWVhbih5X3Rlc3QpIC0geV90ZXN0KV4yKQpwcmludChwYXN0ZSgiUlNRIHRlc3QgOiIscnNxKSkKIyBDYWxjdWxhdGUgYWRqdXN0ZWQgUi1zcXVhcmVkCm4gPC0gbGVuZ3RoKHlfdGVzdCkgIyBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCnAgPC0gY29lZihmaW5hbF9tb2RlbCwgcyA9ICJsYW1iZGEubWluIikgIyBudW1iZXIgb2YgcHJlZGljdG9ycwphZGpfcnNxIDwtIDEgLSAoMSAtIHJzcSkgKiAobiAtIDEpIC8gKG4gLSBsZW5ndGgocCkgLSAxKQoKcHJpbnQocGFzdGUoIkFkanVzdGVyIFIgc3F1cmUgOiIsYWRqX3JzcSkpCmBgYAoKZm9yIHNxcmUgdGFyZ2V0CmBgYHtyfQoKbGlicmFyeShjYXJldCkKbGlicmFyeShnbG1uZXQpCgoKeF90cmFpbiA8LSBtb2RlbC5tYXRyaXgoc2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpb34uLCB0cmFpbkRhdGFfc3FydClbLC0xXSAjIEV4Y2x1ZGUgaW50ZXJjZXB0IGNvbHVtbgp5X3RyYWluIDwtIHRyYWluRGF0YV9zcXJ0JHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8KeF90ZXN0IDwtIG1vZGVsLm1hdHJpeChzY2FsZWRfc3FydF9zdWljaWRlX3JhdGlvfi4sIHRlc3REYXRhX3NxcnQpWywtMV0gIyBFeGNsdWRlIGludGVyY2VwdCBjb2x1bW4KeV90ZXN0IDwtIHRlc3REYXRhX3NxcnQkc2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpbwoKIyBEZWZpbmUgdGhlIGNyb3NzLXZhbGlkYXRpb24gZXhwZXJpbWVudApjdmZpdCA8LSBjdi5nbG1uZXQoeF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAxLCB0eXBlLm1lYXN1cmUgPSAibXNlIikKCiMgR2V0IHRoZSBvcHRpbWFsIGxhbWJkYSB2YWx1ZQpsYW1iZGFfb3B0aW1hbCA8LSBjdmZpdCRsYW1iZGEubWluCgojIFRyYWluIHRoZSBmaW5hbCBtb2RlbCB1c2luZyB0aGUgb3B0aW1hbCBsYW1iZGEKZmluYWxfbW9kZWwgPC0gZ2xtbmV0KHhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSwgbGFtYmRhID0gbGFtYmRhX29wdGltYWwpCgoKcHJlZGljdGlvbnMgPC0gcHJlZGljdChmaW5hbF9tb2RlbCwgcyA9IGxhbWJkYV9vcHRpbWFsLCBuZXd4ID0geF90ZXN0KQoKIyBFdmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2UKbXNlIDwtIG1lYW4oKHByZWRpY3Rpb25zIC0geV90ZXN0KV4yKQoKcHJpbnQocGFzdGUoIk1TRSBvbiB0aGUgdGVzdCBzZXQ6ICIsIG1zZSkpCgpyc3EgPC0gMSAtIHN1bSgocHJlZGljdGlvbnMgLSB5X3Rlc3QpXjIpIC8gc3VtKChtZWFuKHlfdGVzdCkgLSB5X3Rlc3QpXjIpCnByaW50KHBhc3RlKCJSU1EgdGVzdCA6Iixyc3EpKQojIENhbGN1bGF0ZSBhZGp1c3RlZCBSLXNxdWFyZWQKbiA8LSBsZW5ndGgoeV90ZXN0KSAjIG51bWJlciBvZiBvYnNlcnZhdGlvbnMKcCA8LSBjb2VmKGZpbmFsX21vZGVsLCBzID0gImxhbWJkYS5taW4iKSAjIG51bWJlciBvZiBwcmVkaWN0b3JzCmFkal9yc3EgPC0gMSAtICgxIC0gcnNxKSAqIChuIC0gMSkgLyAobiAtIGxlbmd0aChwKSAtIDEpCgpwcmludChwYXN0ZSgiQWRqdXN0ZXIgUiBzcXVyZSA6IixhZGpfcnNxKSkKYGBgCgoKIyMjIDQuNC4zIFJpZGdlIHJlZ3Jlc3Npb24KUmlkZ2UgcmVncmVzc2lvbiwgYWxzbyBrbm93biBhcyBUaWtob25vdiByZWd1bGFyaXphdGlvbiwgaXMgYSByZWd1bGFyaXphdGlvbiB0ZWNobmlxdWUgZGVzaWduZWQgdG8gZGVhbCB3aXRoIG11bHRpY29sbGluZWFyaXR5LCBpbXByb3ZlIHByZWRpY3Rpb24gYWNjdXJhY3ksIGFuZCBpbnRlcnByZXRhYmlsaXR5IG9mIHRoZSBzdGF0aXN0aWNhbCBtb2RlbCBpdCBpcyBhcHBsaWVkIHRvLiBSaWRnZSByZWdyZXNzaW9uIHBlcmZvcm1zICJMMiByZWd1bGFyaXphdGlvbiwiIHdoaWNoIG1lYW5zIHRoYXQgaXQgYWRkcyBhIHBlbmFsdHkgZXF1aXZhbGVudCB0byB0aGUgc3F1YXJlIG9mIHRoZSBtYWduaXR1ZGUgb2YgdGhlIGNvZWZmaWNpZW50cy4gVGhpcyByZXN1bHRzIGluIHNtYWxsZXIgY29lZmZpY2llbnRzLCB3aGljaCBtYWtlcyB0aGUgbW9kZWwgbGVzcyBjb21wbGV4IGFuZCBiZXR0ZXIgYXQgZ2VuZXJhbGl6aW5nIGZyb20gdGhlIHRyYWluaW5nIGRhdGEgdG8gdW5zZWVuIGRhdGEuCgpmb3IgbG9nIHRhcmdldApgYGB7cn0KCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZ2xtbmV0KQoKCnhfdHJhaW4gPC0gbW9kZWwubWF0cml4KHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpb34uLCB0cmFpbkRhdGFfbG9nKVssLTFdICMgRXhjbHVkZSBpbnRlcmNlcHQgY29sdW1uCnlfdHJhaW4gPC0gdHJhaW5EYXRhX2xvZyRzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8KeF90ZXN0IDwtIG1vZGVsLm1hdHJpeChzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW9+LiwgdGVzdERhdGFfbG9nKVssLTFdICMgRXhjbHVkZSBpbnRlcmNlcHQgY29sdW1uCnlfdGVzdCA8LSB0ZXN0RGF0YV9sb2ckc2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvCgojIERlZmluZSB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBleHBlcmltZW50CmN2Zml0IDwtIGN2LmdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAsIHR5cGUubWVhc3VyZSA9ICJtc2UiKQoKIyBHZXQgdGhlIG9wdGltYWwgbGFtYmRhIHZhbHVlCmxhbWJkYV9vcHRpbWFsIDwtIGN2Zml0JGxhbWJkYS5taW4KCiMgVHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBvcHRpbWFsIGxhbWJkYQpmaW5hbF9tb2RlbCA8LSBnbG1uZXQoeF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLCBsYW1iZGEgPSBsYW1iZGFfb3B0aW1hbCkKCgpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGZpbmFsX21vZGVsLCBzID0gbGFtYmRhX29wdGltYWwsIG5ld3ggPSB4X3Rlc3QpCgojIEV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZQpybXNlIDwtbWVhbigocHJlZGljdGlvbnMgLSB5X3Rlc3QpXjIpCgpwcmludChwYXN0ZSgiTVNFIG9uIHRoZSB0ZXN0IHNldDogIiwgcm1zZSkpCgpyc3EgPC0gMSAtIHN1bSgocHJlZGljdGlvbnMgLSB5X3Rlc3QpXjIpIC8gc3VtKChtZWFuKHlfdGVzdCkgLSB5X3Rlc3QpXjIpCnByaW50KHBhc3RlKCJSU1EgdGVzdCA6Iixyc3EpKQojIENhbGN1bGF0ZSBhZGp1c3RlZCBSLXNxdWFyZWQKbiA8LSBsZW5ndGgoeV90ZXN0KSAjIG51bWJlciBvZiBvYnNlcnZhdGlvbnMKcCA8LSBjb2VmKGZpbmFsX21vZGVsLCBzID0gImxhbWJkYS5taW4iKSAjIG51bWJlciBvZiBwcmVkaWN0b3JzCmFkal9yc3EgPC0gMSAtICgxIC0gcnNxKSAqIChuIC0gMSkgLyAobiAtIGxlbmd0aChwKSAtIDEpCgpwcmludChwYXN0ZSgiQWRqdXN0ZXIgUiBzcXVyZSA6IixhZGpfcnNxKSkKYGBgCmZvciBzcXJ0IHRhcmdldCAKYGBge3J9CnhfdHJhaW4gPC0gbW9kZWwubWF0cml4KHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW9+LiwgdHJhaW5EYXRhX3NxcnQpWywtMV0gIyBFeGNsdWRlIGludGVyY2VwdCBjb2x1bW4KeV90cmFpbiA8LSB0cmFpbkRhdGFfc3FydCRzY2FsZWRfc3FydF9zdWljaWRlX3JhdGlvCnhfdGVzdCA8LSBtb2RlbC5tYXRyaXgoc2NhbGVkX3NxcnRfc3VpY2lkZV9yYXRpb34uLCB0ZXN0RGF0YV9zcXJ0KVssLTFdICMgRXhjbHVkZSBpbnRlcmNlcHQgY29sdW1uCnlfdGVzdCA8LSB0ZXN0RGF0YV9zcXJ0JHNjYWxlZF9zcXJ0X3N1aWNpZGVfcmF0aW8KCiMgRGVmaW5lIHRoZSBjcm9zcy12YWxpZGF0aW9uIGV4cGVyaW1lbnQKY3ZmaXQgPC0gY3YuZ2xtbmV0KHhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgdHlwZS5tZWFzdXJlID0gIm1zZSIpCgojIEdldCB0aGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUKbGFtYmRhX29wdGltYWwgPC0gY3ZmaXQkbGFtYmRhLm1pbgoKIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgdXNpbmcgdGhlIG9wdGltYWwgbGFtYmRhCmZpbmFsX21vZGVsIDwtIGdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAsIGxhbWJkYSA9IGxhbWJkYV9vcHRpbWFsKQoKCnByZWRpY3Rpb25zIDwtIHByZWRpY3QoZmluYWxfbW9kZWwsIHMgPSBsYW1iZGFfb3B0aW1hbCwgbmV3eCA9IHhfdGVzdCkKCiMgRXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlCnJtc2UgPC0gbWVhbigocHJlZGljdGlvbnMgLSB5X3Rlc3QpXjIpCgpwcmludChwYXN0ZSgiTVNFIG9uIHRoZSB0ZXN0IHNldDogIiwgcm1zZSkpCgpyc3EgPC0gMSAtIHN1bSgocHJlZGljdGlvbnMgLSB5X3Rlc3QpXjIpIC8gc3VtKChtZWFuKHlfdGVzdCkgLSB5X3Rlc3QpXjIpCnByaW50KHBhc3RlKCJSU1EgdGVzdCA6Iixyc3EpKQojIENhbGN1bGF0ZSBhZGp1c3RlZCBSLXNxdWFyZWQKbiA8LSBsZW5ndGgoeV90ZXN0KSAjIG51bWJlciBvZiBvYnNlcnZhdGlvbnMKcCA8LSBjb2VmKGZpbmFsX21vZGVsLCBzID0gImxhbWJkYS5taW4iKSAjIG51bWJlciBvZiBwcmVkaWN0b3JzCmFkal9yc3EgPC0gMSAtICgxIC0gcnNxKSAqIChuIC0gMSkgLyAobiAtIGxlbmd0aChwKSAtIDEpCgpwcmludChwYXN0ZSgiQWRqdXN0ZXIgUiBzcXVyZSA6IixhZGpfcnNxKSkKYGBgCmBgYHtyfQp2ZWMxIDwtYygiU2ltcGxlIExpbmVhciIsMC4yNTk2LDAuNzEzOCwwLjcwNTIsImxvZ19zdWljaWRlX3JhdGlvIikKdmVjMjwtYygiU2ltcGxlIExpbmVhciIsMC4yNzgsMC42NTc4LDAuNjQ3NSwic3FydF9zdWljaWRlX3JhdGlvIikKdmVjMzwtYygiU2ltcGxlIExpbmVhciIsMTQzLjE4LDAuNDg1LDAuNDcwMywic3VpY2lkZV9yYXRpbyIpCnZlYzQ8LWMoIkxhc3NvIiwwLjI2MDQsMC43MTI5LDAuNzA0MiwibG9nX3N1aWNpZGVfcmF0aW8iKQp2ZWM1PC1jKCJMYXNzbyIsMC4yNzk4LDAuNjU1LDAuNjQ1Mywic3FydF9zdWljaWRlX3JhdGlvIikKdmVjNjwtYygiUmlkZ2UiLDAuMjYxNSwiMC43MTE2IiwiMC43MDI4IiwibG9nX3N1aWNpZGVfcmF0aW8iKQp2ZWM3PC0gYygiUmlkZ2UiLDAuMjc1OCwwLjY2MDcsMC42NTAzLCJzcXJ0X3N1aWNpZGVfcmF0aW8iKQpgYGAKCiMgNS5Db25jbHVzaW9ucwoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoTW9kZWw9cmVwKE5BLDcpLCAKICAgICAgICAgICAgICAgICBNZWFuX1NxdWFyZWRfRXJyb3I9cmVwKE5BLDcpLCAKICAgICAgICAgICAgICAgICBSX3NxdWFyZWQ9cmVwKE5BLDcpLCAKICAgICAgICAgICAgICAgICBBZGp1c3RlZF9SX3NxdWFyZWQ9cmVwKE5BLDcpLCAKICAgICAgICAgICAgICAgICBUYXJnZXQ9cmVwKE5BLDcpKQpgYGAKYGBge3J9CmRmWzEsIF0gPC0gdmVjMQpkZlsyLCBdIDwtIHZlYzIKZGZbMywgXSA8LSB2ZWMzCmRmWzQsIF0gPC0gdmVjNApkZls1LCBdIDwtIHZlYzUKZGZbNiwgXSA8LSB2ZWM2CmRmWzcsIF0gPC0gdmVjNwpgYGAKCmBgYHtyfQpkZgpgYGAKVGhlIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGxvZyBhcyB0aGUgdGFyZ2V0IHNsaWdodGx5IG91dHBlcmZvcm1zIHRoZSBvdGhlcnMuIEhvd2V2ZXIsIHNpbXBsZSBsaW5lYXIsIExhc3NvLCBhbmQgUmlkZ2UgcmVncmVzc2lvbnMgd2l0aCBsb2cgYXMgdGhlIHRhcmdldCBkZW1vbnN0cmF0ZWQgcXVpdGUgc2ltaWxhciBwZXJmb3JtYW5jZS4gVG8gZGlzdGluZ3Vpc2ggbW9yZSBlZmZlY3RpdmVseSBiZXR3ZWVuIHRoZXNlIG1vZGVscywgd2Ugc2hvdWxkIGVtcGxveSBjcm9zcy12YWxpZGF0aW9uIHRlY2huaXF1ZXMuCgpOb3csIGxldCdzIGV4cGxvcmUgdGhlIGltcG9ydGFuY2Ugb2YgZWFjaCBmZWF0dXJlIGluIHRoZSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIHdoZXJlIHRoZSB0YXJnZXQgaXMgJ2xvZ19zdWljaWRlX3JhdGlvJy4gVGhpcyB3aWxsIGdpdmUgdXMgbW9yZSBpbnNpZ2h0IGludG8gdGhlIHNpZ25pZmljYW50IHByZWRpY3RvcnMgaW4gb3VyIG1vZGVsLgpgYGB7cn0KbGlicmFyeShNZXRyaWNzKQpmb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUoInNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbyIsICJ+IiwgcGFzdGUoc2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvX3ZhciwgY29sbGFwc2UgPSAiICsgIikpKQoKbW9kZWwgPC0gbG0oZm9ybXVsYSwgZGF0YSA9IHRyYWluRGF0YV9sb2cpCmRhdGFfdGVzdCA8LSB0ZXN0RGF0YV9sb2cKIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0aW5nIGRhdGEKcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IHRlc3REYXRhX2xvZykKdGFyZ2V0IDwtInNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbyIKIyBDYWxjdWxhdGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpCm1zZSA8LSBtc2UodGVzdERhdGFfbG9nJHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbywgcHJlZGljdGlvbnMpCnByaW50KHBhc3RlMCgiTVNFOiAiLCBtc2UpKQoKIyBDYWxjdWxhdGUgUi1zcXVhcmVkCnNzZSA9IHN1bSgocHJlZGljdGlvbnMgLSB0ZXN0RGF0YV9sb2ckc2NhbGVkX2xvZ19zdWljaWRlX3JhdGlvKV4yKQpzc3QgPSBzdW0oKHRlc3REYXRhX2xvZyRzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8gLSBtZWFuKHRlc3REYXRhX2xvZyRzY2FsZWRfbG9nX3N1aWNpZGVfcmF0aW8pKV4yKQpyX3NxdWFyZWQgPSAxIC0gc3NlIC8gc3N0CnByaW50KHBhc3RlMCgiUi1zcXVhcmVkOiAiLCByX3NxdWFyZWQpKQoKIyBDYWxjdWxhdGUgQWRqdXN0ZWQgUi1zcXVhcmVkCm4gPSBsZW5ndGgodGVzdERhdGFfbG9nJHNjYWxlZF9sb2dfc3VpY2lkZV9yYXRpbykgIyBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCnAgPSBsZW5ndGgoY29lZihtb2RlbCkpIC0gMSAjIG51bWJlciBvZiBwcmVkaWN0b3JzCmFkanVzdGVkX3Jfc3F1YXJlZCA9IDEgLSAoMSAtIHJfc3F1YXJlZCkgKiAoKG4gLSAxKSAvIChuIC0gcCAtIDEpKQpwcmludChwYXN0ZTAoIkFkanVzdGVkIFItc3F1YXJlZDogIiwgYWRqdXN0ZWRfcl9zcXVhcmVkKSkKCmBgYApgYGB7cn0Kc3VtbWFyeShtb2RlbCkKYGBgCmFzIHdlIGNhbiBzZWUgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmUgaW4gb3JkZXIgYXJlOgoKMS4gc2V4CjIuIGNvdW50cnkKMy4gYWdlCjQuIHNjYWxlZF9hdmdfdGVtcAo1LiBzY2FsZWRfbG9nX0dEUF9jYXBpdGEKNi4gcG9wdWxhdGlvbl9iaW5lX2plbmtzCg==